线程管理
启动线程
线程在 std::thread
对象创建(为线程指定任务)时启动
1
2
3
4
5
6
7
8
9
10
11
12
13
|
void myprint()
{
cout<<"子线程"<<endl;
//...
cout<<"线程结束"<<endl;
return ;
}
int main()
{
std::thread mytobj(myprint);
mytobj.join();
return 1;
}
|
等待线程完成
调用.join()
方法,可以要求主线程等待该线程完成后再退出
想象另一种情景,你需要主线程等待一段特定的时间、或者时不时检查以下线程是否仍在运行,则需要其他更为复杂的机制来完成,比如条件变量和期待。
.joinable()
方法可以判断,当前线程对象是否可以加入(一个线程对象,不可重复加入)
特殊情况下的等待
相比于随时可以执行的.detach()
, .join()
在使用过程中则需要考量更多的问题,因为在调用.join()
之前,运行的线程发生了异常,就会跳过.join()
两种方法
- 在函数异常处理中join
- 利用RAII
后台运行线程
使用detach()
会让线程在后台运行,这就意味着主线程不能与之产生直接交互。也就是说,不会等待这个线程结束;如果线程分离,那么就不可能有 std::thread
对象能引用它,分离线程的确在后台运行,所以分离线程不能被加入。不过C++运行库保证,当线程退出时,相关资源的能够正确回收,后台线程的归属和控制C++运行库都会处理。
⚠️必须在 std::thread 对象销毁之前做出决定,否则你的程序将会终止( std::thread 的析构函数会调用 std::terminate() ,这时再去决定会触发相应异常)。
向线程函数传递参数
传递简单数据类型
std::thread 的入口函数中传递简单类型数据,如 int 等,本质上是值传递,可以放心的 detach,如下图:

如上如图,虽然是引用接受参数,但线程中参数的地址和主线程中参数地址却不一样,是拷贝了一个新的参数。
传递指针
std::thread 的入口函数中传递指针时,要小心使用 detach,如下图:

如上图,线程函数中参数首地址和形参首地址一样,要避免使用 detach()。
传递类对象
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
#include <iostream>
#include <thread>
using namespace std;
class TA{
void operator()()
{
cout<<"TA::operator()开始执行"<<endl;
cout<<"TA::operator()结束执行"<<endl;
}
};
int main()
{
TA ta;
thread mytobj(ta); //里不可以是临时对象TA(),否则编译无法通过
mytobj.join();
cout<<"main主函数结束执行"<<endl;
return 0;
}
|
std::thread 的入口函数中传递类对象时,本质上也是传递的类对象的拷贝。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
|
#include "thread"
#include "iostream"
using namespace std;
class demoClass
{
public:
demoClass(int var) :m_var(var)
{
cout << "构造函数执行,线程id:" << this_thread::get_id() << endl;
}
demoClass(const demoClass& demo) :m_var(demo.m_var)
{
cout << "拷贝构造函数执行,线程id:" << this_thread::get_id() << endl;
}
~demoClass()
{
cout << "析构函数执行,线程id:" << this_thread::get_id() << endl;
}
private:
int m_var;
};
void functionToThread(const demoClass & demo)
{
cout << "线程启动......" << endl;
cout << "线程id:" << this_thread::get_id() << " demo 的地址 "<< &demo << endl;
cout << "线程结束......" << endl;
}
int main()
{
demoClass demo(20);
cout << "线程id:" << this_thread::get_id() << " demo 的地址 " << &demo << endl;
thread myThread(functionToThread, demo);
myThread.join();
cout << "主线程结束......" << endl;
system("pause");
return 0;
}
|
执行结果如下:虽然是引用接受参数,但主线程中参数对象的地址和传入子线程中对象地址不一样:

④.传递类对象的引用
使用 std::ref() 传递对象,则传递的时真引用,如下图:

⑤.传递临时对象
当 std::thread 的入口函数中参数通过隐式类型转换产生临时对象时,临时对象的构造是在子线程中执行的,如下图:

可以通过显示的调用构造函数来生成临时对象,来强制使类型转换在主线程中执行,如下图:
