线程的创建

三、线程启动、结束、创建线程多个方法

本节主要记录std::thread类以及join()detach()方法。

1、线程创建

主线程从main()函数开始执行,我们自己创建的线程,也需要从一个函数(初始函数)开始运行。一旦这个函数运行完毕,代表这个线程运行结束。

整个进程是否执行完毕的标志是:主线程是否执行完毕,如果主线程执行完毕,代表整个进程执行完毕了。一般情况下:如果其他子线程还没有执行完毕,那么这些子线程也会被操作系统强行终止。

一般情况下:如果想保持子线程的运行状态,那么要让主线程一直保持运行。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#include <thread>                       // 1、包含头文件
#include <iostream>

using namespace std;
void myprint(){ // 2、初始函数要写
cout <<"我的线程开始执行"<<endl;
// ...
cout<<"我的线程执行完毕"<<endl;
}

int main(){ // 3、mian中开始写代码,myprint是一个可调用对象
thread mytobj(myprint); /* 这条语句创建了线程,线程开始执行。 */
mytobj.join(); /* 阻塞主线程,让主线程等待子线程执行完毕,然后子线程和主线程汇合。*/
/* 主线程继续往下走 */
// mytobj.detach()
cout<<"Hello World"<<endl; // 4、这个代码有两个线程在跑
return 0;
}

thread是个标准库里的类。

2、detach()

一般情况下主线程有义务等待子线程执行完毕,最终主线程安全正常退出。但是C++11中的detach()可以实现分离:也就是主线程不和子线程汇合了,主线程不必等子线程运行结束。一旦detach()之后,与主线程关联的thread对象就会失去与主线程的关联,此时子线程就会驻留在后台运行,这个子线程就相当于被C++运行时库接管了,当这个子线程执行完毕后,会由运行时库负责清理该线程的相关资源(驻留后台,Linux中的守护进程)。

一旦调用了detach()(犹如脱缰的野马,不受控制了),就不能再用join(),否则系统会报异常。

3、joinable()

判断是否可以成功使用join()或者detach()函数,可以返回true。

4、其他创建线程的手法

创建不同的可调用对象:

使用类,类对象作为可调用对象(函数对象):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class TA {
public:
TA(int & num):num_(num){}
void operator()() { // 不能有参数
cout << "我的线程仿函数开始执行了" << endl;
// ...
cout << "我的线程仿函数结束执行" << endl;
cout << "num的数为:"<<num_<<endl;
}

int & num_; // 使用引用会导致出达问题
};

int main(){
int b = 6;
TA ta(b);
thread mytobj2(ta); // ta对象是被复制到线程中去的(copy构造函数),不受主线程中的ta被销毁的影响
mytobj2.join();
// mytobj2.detach(); // 不受控制,例如主线程先执行完,变量b被回收掉了,这时候TA类型按照引用传递,
return 0; // operator()会出现访问被释放的资源,所以类中使用引用要注意!
}

lambda表达式(匿名函数)也是一种可调用对象:

1
2
3
4
auto mylambdathread = [](){
cout <<"lambda表达式创建的线程"<<endl;
// ...
}
听说打赏我的人,最后都找到了真爱。