join
主线程等待子线程执行完
#include <thread>
using namespace std;
/*
a、每个进程都有一个主线程,这个主线程是唯一的,也就是一个进程只有一个主线程
b、当你执行一个可执行程序,产生一个进程后,这个主线程就随着这个进程默默的启动了
多线程(并发)
//线程并不是越多越好,每一个线程,都需要一个独立的堆栈空间(1M),线程之间的切换要保存很多中间状态
*/
void myprint()
{
cout << "开始执行线程" << endl;
}
int main()
{
//主线程从main函数开始执行
//一般情况下,主线程执行完毕了,如果其他子线程还没有执行完毕,那么这些子线程也会被操作系统强行终止
thread mythread(myprint);//1、创建了线程 2、线程执行起点myprint 3、myprint线程开始执行
mythread.join();//希望主线程等待子线程,等待子线程执行完、阻塞主线程
cout << "main func end" << endl;
return 0;
}
detach
int main()
{
thread mythread(myprint);
//这个子线程就相当于被c++运行时库时刻接管,当这个子线程执行完成后,由运行时库负责清理该线程相关的资源
mythread.detach();//主线程与子线程失去关联,子线程在后台运行
//主线程运行结束,那进程就结束了,子线程的打印就不输出了,在后台执行
for (int i = 0; i < 10; i++) {
cout << "main func end" << i << endl;
}
return 0;
}
当调用了detach后,不能再调用join了
joinable
int main()
{
thread mythread(myprint);
//判断detach 或者 join能否被使用,如果返回true 就可以使用
if (mythread.joinable()) {
cout << "11111" << endl;
mythread.join();
}
//当join 或者 detach被调用了,就返回false
if (mythread.joinable()) {
cout << "22222" << endl;
mythread.detach();
}
for (int i = 0; i < 10; i++) {
cout << "main func end" << i << endl;
}
return 0;
}
线程传参陷阱
创建线程,不要在参数中传递指针、引用
void myprint(const int &i, char * mybuf)
{
cout << i << endl;
cout << mybuf << endl;
}
int main()
{
int var = 10;
int& myvar = var;
char buf[] = "this is buffer";
//myvar这里是引用,本应该不会进行拷贝,但是确拷贝了,
//所以就算主线程结束了,myvar回收了, myprint函数中的i也不会报错
//线程对参数中的引用也会进行拷贝
//但是指针不会拷贝,如果主线程的buf回收了,myprint中的 mybuf就会报错
thread mythread(myprint, myvar,buf);
mythread.detach();
return 0;
}
在thread的参数中 创建临时对象传递给线程,一定能保证在主线程执行完之前,创建完成,thread中的参数,引用也会被拷贝一次
class A
{
public:
A(int i) :m_i(i) { cout << "传参构造函数" << this << endl; }
A(const A& a) :m_i(a.m_i) {cout << "拷贝构造函数" << this << endl; }
~A() { cout << "析构函数" << this << endl; }
int m_i;
};
void myprint(const int i, A a)
{
cout << a.m_i << endl;
}
int main()
{
int var = 10;
int& myvar = var;
char buf[] = "this is buffer";
//添加A 临时对象,确保在main函数执行完之前,就能构造类对象
//这样就执行了一次 创建临时对象,传参构造函数 和一次拷贝构造函数,
thread mythread(myprint,var,A(var));
mythread.detach();
return 0;
}
通过线程id看是在主线程中做的创建临时对象还是在 子线程创建的临时对象
带入普通变量,隐式转换成A类对象
class A
{
public:
A(int i) :m_i(i) { cout << "传参构造函数" << this << "thread id =" << this_thread::get_id() << endl; }
A(const A& a) :m_i(a.m_i) {cout << "拷贝构造函数" << this << "thread id =" << this_thread::get_id() << endl; }
~A() { cout << "析构函数" << this << "thread id =" << this_thread::get_id() << endl; }
int m_i;
};
void myprint(const int i, A a)
{
cout << a.m_i << endl;
}
int main()
{
int var = 10;
//主线程id
cout << "主线id是" << this_thread::get_id() << endl;
thread mythread(myprint,var,var);
mythread.join();
return 0;
}
说明带入变量做隐式转换,创建A类是在子线程中完成,如果主线程执行结束了,释放了var,就会出问题
带入A类对象,传参构造
说明创建临时变量,构造函数是在主线程中完成的
如何避免线程参数 进行拷贝构造呢?
有时候带入的是引用,并且修改了引用的值,但是如果进行了拷贝构造,那么引用就没有意义了,修改的值 也仅仅只是修改 拷贝后对象的值, 如果 禁止拷贝构造?
仅仅需要在thread中的 参数 加上 ref就可以
void myprint(A &a)
{
a.m_i = 20;
cout << "myprint" << a.m_i << endl;
return;
}
int main()
{
int var = 10;
//主线程id
cout << "主线id是" << this_thread::get_id() << endl;
A a(var);
cout << "111 a.m_i = " << a.m_i << endl;
//加上ref后,就不会对a进行拷贝了,m_1也能被成功修改
thread mythread(myprint,ref(a));
mythread.join();
cout << "222 a.m_i = " << a.m_i << endl;
return 0;
}