std::thread介绍
std::thread是c++11的特性,提供管理多线程的类。
note: 注意下文中线程和线程对象的区别:线程即通俗意义上的线程,而**线程对象(thread对象)**这里指的是thread类的一个具体实例
thread的常用成员函数
- detach(): 将线程和thread对象分离,分离后线程和线程对象没有任何联系
- join(): 连接线程,并等待线程执行完毕才返回。
- joinable(): 判断线程线程对象是否可连接,如果thread对象管理着某个线程(不管这个线程有没有在执行),则为可连接状态,反之则不是。
- thread& operator=(thread&& _Other):右值引用赋值操作,实现将临时thread对象赋值给thread对象。
- swap:和另一个thread对象交换管理的线程
- native_handle:获得线程的句柄
详细说明
- thread对象和线程是一对一的关系(通过删除拷贝构造函数和左值赋值函数实现,见下面的源码),即一个thread对象只能管理一个线程,当然也可以不管理任何线程,比如使用默认构造函数,move构造,join或者detach之后,线程对象就不再管理线程。
- thread的“=”操作,实际上是通过右值引用实现了对临时线程对象的控制权转移,即将线程的管理权从一个临时的线程对象移动到另一线程对象。注:线程对象删除了左值引用赋值的操作。这意味着只能将一个临时对象赋值给线程对象,而不能将一个线程对象赋值给另一个线程变量。这也符合其对象和线程一一对应的设定。
其删除了用左值进行赋值的操作,源码如下:
thread& operator=(thread&& _Other) _NOEXCEPT
{ // move from _Other
return (_Move_thread(_Other));
}
thread(const thread&) = delete; //删除拷贝构造函数
thread& operator=(const thread&) = delete; //删除了左值引用的赋值操作
所以有如下程序段的问题:
std::thread t1;
std::thread t2;
t1 = t2; //错误:左值的赋值操作已被删除
t1 = std::thread(fun); //正确
所以thread对象获得线程管理权有两种方式:1)通过构造函数;2)通过右值赋值操作。
deach()
操作使线程独立于thread对象执行,调用后线程和该thread对象没有任何关系。通过get_id()
获得线程id = 0(即没有线程)。joinable()
和join()
可以用于在销毁销毁thread对象前,等待线程执行结束,避免主线程退出时,其他线程还在继续执行,并访问主线程的相关数据,而导致程序崩溃。- 线程对象绑定具体的线程后一定要在thread对象销毁之前,执行
detach()
或者join()
,否则程序会崩溃。崩溃的原因:thread对象析构时会调用如下函数,如果线程对象之前没有调用detach(
)或者join()
方法,析构时会执行terminate()
,导致程序崩溃。
thread& _Move_thread(thread& _Other)
{ // move from _Other
if (joinable())
_XSTD terminate(); //崩溃原因:terminate()用于终止程序运行。
_Thr = _Other._Thr;
_Thr_set_null(_Other._Thr);
return (*this);
}
这也符合thread作为线程管理对象的设定,析构时如果线程还在运行就不能被析构,否则其管理职责就失去了意义。
thread具体使用方法
创建线程
#include <iostream>
#include <string>
#include <thread>
#include <windows.h>
using namespace std;
void fun1(){
while (true) {
cout << "run in sub thread!"<<endl;
Sleep(100);
}
}
void fun2(const std::string &str) {
while (true) {
cout << "run in sub thread with para:" << str<<endl;
Sleep(100);
}
}
void main()
{
//通过构造函数创建线程
std::thread t1(fun1);
//创建线程,并传入参数
std::thread t2(fun2, "subthread2");
//用lambda表达式创建线程
std::thread t3([]() {
while (true) {
cout << "run in lambda exp thread" << endl;
Sleep(100);
}
});
t1.detach(); t2.detach(); t3.detach();
system("pause");
}
类中使用thread
经常我们需要在执行某个类的方法时开启一个子线程,如果子线程中使用了对象的成员变量,则需要保证在类对象析构前执行完子线程的内容,否则会报内存访问的错误。可以通过如下方式解决这个问题:
下面的例子中,在类的mfun方法中开启了子线程,线程对象并没马上调用detach()或者join()操作,而是在类析构函数中调用,这样就可以保证等子线程执行结束后再析构类的对象。
class ThreadUser {
public:
~ThreadUser(){
if (subThread.joinable()) //等待线程执行完毕在释放thread对象
subThread.join();
}
void mfun() {
subThread = thread([&]() {
for(int i = 0; i < num; i++)
cout <<"i"<<endl;
});
}
private:
thread subThread;
int num;
};
类的成员函数作为线程执行函数
类成员函数可以作为thread的线程函数进行执行,但需要传入类的指针作为参数,方法如下:
class ThreadUser {
public:
void tfun() {
cout << "threadsuser fun" << endl;
}
};
void main() {
thread tr(&ThreadUser::tfun, new ThreadUser()); //用ThreadUser对象的成员函数tfun作为线程函数,&<类名>::<成员函数名> 为获得类的成员函数指针的写法。
system("pause");
}