C++11多线程(一)线程入口、传参、join()和detach()

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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值