1. C++使用Thread类创建多线程的三种方式

1. 说明

使用Thread类创建线程对象的同时就会立刻启动线程,线程启动之后就要明确是等待线程结束(join)还是让其自主运行(detach),如果在线程销毁前还未明确上面的问题,程序就会报错。一般都会选择等待线程执行结束,如果不等待线程,即让其与主线程分离自主运行,那必须保证线程结束之前,可访问数据的有效性。
注意:当主线程碰到了子线程调用join()函数,程序会阻滞在当前行等待,直到当前子线程执行结束才会继续执行下面的代码。如果有多个线程需要同时执行,需要将每个线程的join()函数放到主线程的最后,才能避免阻滞现象。

2. 创建线程

使用thread类创建线程,主要有三种方式:①使用普通函数创建,②使用自定义类创建,③使用Lambda表达式创建

2.1 使用普通函数创建

此种方式,只是将普通函数的名字传输到thread构造函数中,注意不要再函数名字后面带()。

#include <iostream>
using namespace std;

#include <thread>

void myPrint() 
{
    
    
    cout << "我的线程开始执行了..." << endl;

    cout << "我的线程执行完毕了..." << endl;
}

int main()
{
    
    
    thread myThread(myPrint);//创建线程对象,并启动线程

    myThread.join();//等待子线程执行结束后,再执行下面的代码
	//myThread.detach();  //子线程与主线程分离,各自执行,互不影响(但子线程如果使用了主线程中的数据,当主线程结束后,子线程无法访问到数据时,会报错)

    std::cout << "主线程收尾,最终主线程安全正常退出..."<<endl;

    return 0;
}

2.2 使用自定义类创建

这种方法需要重载()运算符,把子线程需要执行的代码写到里面即可。而且此种方法将类对象名传输到thread类构造函数中后,实际上时调用了自定义类的拷贝构造函数复制了一份,线程执行结束后会调用自定义类的析构函数。

#include <iostream>
using namespace std;

#include <thread>

class background_task
{
    
    
public:
	//构造函数
    background_task()
    {
    
    
        cout << "这里时构造函数...." << endl;
    }
	//拷贝构造函数
    background_task(const background_task& bg)
    {
    
    
        cout << "执行了拷贝构造函数...." << endl;
   }
	//析构函数(此案例中析构函数被调用两次,具体解释在下面)
   ~background_task()
   {
    
    
       cout << "执行了析构函数...." << endl;
   }
	//重载()运算符,将子线程需要执行的代码写入其中
   void operator()() const
   {
    
    
       //在这里写子线程要执行的代码
       cout << "我的线程开始执行...." << endl;

       cout << "我的线程执行结束了...." << endl;
   }
};

int main()
{
    
    
    background_task f;//创建类对象(主线程结束后销毁此对象,会调用一次析构函数)
    thread my_thread(f);//创建线程对象,并启动线程(线程结束后,在子线程空间内通过拷贝构造函数创建的对象也会被销毁,也会调用一次析构函数)
    my_thread.join();
    
    std::cout << "主线程收尾,最终主线程安全正常退出..."<<endl;

    return 0;
}

程序输出结果:
在这里插入图片描述

2.3 使用Lambda表达式创建

这种方式创建比较简单,但是子线程中的代码不易重复使用


#include <iostream>
using namespace std;

#include <thread>

int main()
{
    
    
    //使用lambda表达式创建线程

    thread my_thread([] {
    
    

        cout << "lambda表达式创建的线程开始执行....\n" << endl;

        cout << "lambda表达式创建的线程执行结束....\n" << endl;

        });

    my_thread.join();

    std::cout << "主线程收尾,最终主线程安全正常退出...\n"<<endl;

    return 0;
}

3 扩展:

①:
子线程调用join()函数时,可以先使用joinable()函数判断是否能够加入,一个子线程只能加入一次,若能加入则返回true,否则返回false.

#include <iostream>
using namespace std;

#include <thread>

void myPrint() 
{
    
    
    cout << "我的线程开始执行了..." << endl;

    cout << "我的线程执行完毕了..." << endl;
}

int main()
{
    
    
    thread myThread(myPrint);//创建线程对象,并启动线程
	
	if(myThread.joinable())
	{
    
    
		cout << "子线程能够使用join()或者detach()函数" << endl;
		myThread.join();//等待子线程执行结束后,再执行下面的代码
	}
	else
	{
    
    
		cout << "子线程不能够使用join()或者detach()函数" << endl;
	}
    std::cout << "主线程收尾,最终主线程安全正常退出..."<<endl;
    return 0;
}

②:
当有多个子线程需要同时进行时,最好将子线程的join()函数调用放到所有子线程的后面,这样能确保子线程之间不会等待


#include <iostream>
using namespace std;

#include <thread>

int main()
{
    
    
    //使用lambda表达式创建线程

    thread my_thread_1([] {
    
    

        cout << "lambda表达式创建的线程 1 开始执行....\n" << endl;

        cout << "lambda表达式创建的线程 1 执行结束....\n" << endl;

        });
    //my_thread_1.join();//如果在这里调用join,则子线程1执行结束后才能执行子线程2,因为join函数会阻塞后面的代码执行
    thread my_thread_2([] {
    
    

        cout << "lambda表达式创建的线程 2 开始执行....\n" << endl;

        cout << "lambda表达式创建的线程 2 执行结束....\n" << endl;

        });
    
    my_thread_1.join();
    my_thread_2.join();

    std::cout << "主线程收尾,最终主线程安全正常退出...\n"<<endl;

    return 0;
}

猜你喜欢

转载自blog.csdn.net/FY_13781298928/article/details/130199585