6-3 線程傳參詳解,detach()大坑,成員函數做線程函數

一:傳遞臨時對象作爲線程參數

1.1、要避免的陷阱1(解釋1)
1.2、要避免的陷阱2(解釋2)

class A
{
	public:
	mutable int m_i;	//const下也能修改
	//類型轉換構造函數
	A(int a):m_i(a){cout<<"構造函數執行"<<this<<"threadid="<<std::this_thread::get_id()<<endl;}
	A(const A &a):m_i(a.m_i){cout<<"拷貝構造函數執行"<<this<<"threadid="<<std::this_thread::get_id()<<endl}
	~A(){cout<<"析構函數執行"<<this<<"threadid="<<std::this_thread::get_id()<<endl;}
	void thread_work(int num)	//來個參數
	{
		cout<<"子線程thread_work執行"<<this<<"thradid="<<std::this_thrad::get_id()<<endl;
	}
	void operator()(int num)
	{
		cout<<"子線程()執行"<<this<<"thradid="<<std::this_thrad::get_id()<<endl;
	}
};
//void myprint(const int &i, char *pmybuf)
void myprint(const int &i, const string &pmybuf)	//感覺可行其實還是不行
{
	cout<<i<<endl;	//分析認爲,i並不是mvar的引用,實際是值傳遞,那麼我們認爲,即使主線程detach了子線程,那麼子線程中用i仍然是安全的。
	cout<<pmybuf.c_str()<<endl;	//指針在detach子線程時,絕對會有問題
	cout<<*pmybuf<<endl;
	return;
}
void myprint(const int i, const A&pmybuf)
{
	cout<<&pmybuf<<endl;	//打印地址
}
//void myprint2(const A &pmybuf)
void myprint2(unique_ptr<int> pzn)
{
	pmybuf.m_i = 199;//我們修改該值不會影響main函數
	cout<<"子線程myprint2的參數地址是"<<&pmybuf<<"threadid="<<std::this_thread::get_id()<<endl;
}
int main()
{
	int mvar = 1;
	int &mvary = mvar;
	char mybuf[] = "this is a test!";
	thread mytobj(myprint,mvar,mybuf);//但是mybuf到底是在什麼時候轉成string.
	//事實上存在,mybuf都被回收了(main函數執行完了),系統才用mybuf去轉string
	thread mytobj(myprint,mvar,string(mybuf));//這裏直接將mybuf轉換成string對象,這是一個可以保證在線程中用肯定有效的對象
	//mytobj.join();

	//1.2
	int mvar = 1;
	int mysecondpar = 12;
	thread myobj(myprint, mvar, A(mysecondpar));//我們是希望mysecondpar轉成A類型對象傳遞給myprint的第二個參數
	//在創建線程的同時構造臨時對象的方法傳遞參數是可行的;
	//mytojb.join;

	cout<<"主線程id是"<<std::this_thread::get_id()<<endl;
	int myar = 1;
	std::thread myobj(myprint2, A(mvar));	//這樣顯示轉換是安全的
	
	A myobj(10);	//生成一個類對象
	std::thread myobj(myprint2, myobj);//myobj將類對象作爲線程參數
	unique_ptr<int> myp(new int(100));
	std::thread mytobj(myprint2, std::move(myp));
	
	//用成員函數指針做線程函數
	A myobj(10);	//生成一個類對象
	std::thread myobj(&A::thread_work, myobj, 15);//&myobj==std::ref(myobj)
	mytobj.join();	//用成員函數必須用join
	
	//用()作爲線程入口函數
	A myobj(10);	//生成一個類對象
	std::thread myobj( myobj, 15);//&myobj==std::ref(myobj)//用std::ref後就不調用拷貝構造函數了,那麼後續如果調用mytobj.detach()就不安全了;
	mytobj.join();
	
	mytobj.detach();	//子線程和主線程分別執行。
	cout<<"I Love China!"<<endl;
	return 0;
}

1.3、總結
a、若傳遞int這種簡單類型參數,建議都是值傳遞,不要用引用。防止節外生枝。
b、如果傳遞類對象,避免隱式類型轉換。全部都在創建線程這一行就構建出臨時對象來,然後在函數參數裏,用引用來接,否則系統還會多構造一次臨時對象
c、建議不使用detach(),,只使用join();這樣就不存在局部變量失效導致線程對內存的非法引用問題;

二:臨時對象作爲線程參數繼續講,常用測試大法;

2.1、線程id概念:ID是個數字,每個線程(不管是主線程還是子線程)實際上都對應着一個數字,而且每個線程對應的數字都不同。也就是說,不同的線程,它的線程id(數字)必然是不同;
線程id可以用C++標準庫裏的函數來獲取。std::this_thread::get_id()來獲取。
2.2、臨時對象構造時機捕獲

三:傳遞類對象作爲線程參數

正常子線程下修改類成員變量不會影響主線程類成員變量,不管有沒有引用在線程都以拷貝接收類對象
用std::ref可以使對象真正傳引用

四:用成員函數指針做線程函數

在這裏插入圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章