1.三种线程入口
首先包含头文件 #include < thread >
#include <thread>
1)函数
void fun(){ cout<<"线程开始执行"<<endl;}
int main()
{
thread tobj(fun);//子线程被创建并开始执行
...
tobj.join();//子线程将在此处执行完毕
return 0;
}
2)类对象(仿函数)和类的成员函数
class TC
{
int m_i;
public:
TC(int i):m_i(i){}//可以通过构造函数传参
void operator()(int num)//可以在这里传参
{
...
}
void enterThread(int num)//使用类成员函数作入口
{
...
}
}
int main()
{
TC tc;
1.仿函数
thread tobj(tc, 15);//子线程被创建,tc被复制,子线程随时开始执行
//这里的tc是被复制到子线程中去的!
2.
thread tobj(std::ref(tc), 15);//传引用,则不能用detach
3.类的成员函数
thread tobj(&TC::enterThread, tc, 15);//使用类的成员函数作入口
4.
thread tobj(&TC::enterThread, std::ref(tc), 15);//同样可以传引用
thread tobj(&TC::enterThread, &tc, 15);//这里竟然可以这样写,也没问题
...
tobj.join();//子线程将在此处执行完毕
return 0;
}
传引用时应注意:
1.类对象作为参数或仿函数的时候,必须使用ref(),否则编译报错
2.使用类的成员函数的时候,可以使用&
3)lambda表达式
auto lam = []{cout<<"线程开始执行"<<endl;};//注意分号
thread tobj(lam);
tobj.join();
2.join()和detach()
1)join()
主线程等子线程执行完毕
2)detach()
主线程与子线程分道扬镳(失去对子线程的控制)
子线程留在后台运行,由C++运行时库接管,子线程执行完毕后由运行时库清理该线程的资源。
detach()后不能调用join()
使用detach的时候一定要注意,子线程不能操作主线程里的变量。因为主线程可能会先执行完毕,里面的资源会被释放,这时候子线程再去操作就会出错!!!
所以,不能传 指针 到子线程中,只能复制过去。
3)joinable()
判断是否能join或detach。一个线程只能join或detach一次
if(tobj.joinable())
tobj.join();//或者tobj.detach();
3.线程传参
void fun(int a,int b)//这里接的参数是从主线程复制过来的,即使用引用接也没用
{ //如果使用detach,则绝对不能用指针!!!
...
}
main()
{
int m = 10;
int n = 10;
thread tobj(fun, m, n);//这里的m,n会被复制一份传到子线程!!
//复制发生在主线程,形实结合发生在子线程
tobj.join();
return 0;
}
1)传类对象
void fun(const string& pstr)//类对象的传递一般是const引用,子线程不修改类对象,或者使用std::ref()(后文提到)
{
...
}
main()
{
char mstr[] = "my test";
//thread tobj(fun,mstr);//这里的mstr会被复制一份(主线程),然后强制转换成const string&(子线程),
//但若使用detach,这个强制转换可能发生在主函数执行结束后!!
thread tobj(fun,string(mstr));//正确方式,在这里进行转换
tobj.detach();
return 0;
}
总结,参数的复制发生在构造线程时,但把复制的参数与实参结合的时间不确定。猜测其同线程开始执行时间类似。
所以,在使用detach时,一定要在实参处完成所需的强制转换。
扩展到一般
class A
{
int m_i;
public:
A(int i):m_i(i){cout<<"构造函数"<<endl;}
A(const A &i):m_i(i){cout<<"拷贝构造函数"<<endl;}
}
void fun(const A& pa) //这里如果不使用引用,还会再复制一份!!!
{
...
}
main()
{
int a=10;
thread tobj(fun,A(a));//一定要在此处进行类型转换
//这里会首先构造A的临时对象
//再把这个对象复制一份(在主线程中进行)
//然后与形参结合。若形参不使用引用,还会再复制一份(在子线程中进行)
tobj.detach();
return 0;
}
2)传类对象的真引用
若不想使用被复制的,就是要操作引用,则使用std::ref()
void fun(A& pa) //使用std::ref()则不会再复制了
{
...
}
main()
{
A a(10);
thread tobj(fun, std::ref(a));//传递类对象的 真引用
tobj.join();
return 0;
}
3)传智能指针
void fun(unique_ptr<int> pzn) //使用std::ref()则不会再复制了
{
...
}
main()
{
unique_ptr<int> mptr(new int(100))
thread tobj(fun, std::move(mptr)); //使用std::move(),这时mptr空了,子线程的指针指向了new int(100)
tobj.join();//这里不能使用detach()
return 0;
}