6-2 線程啓動、結束,創建線程多法、join,detach

一、範例演示線程運行的開始和結束

程序運行起來,生成一個進程,該進程所屬的主線程開始自動運行;
cout<<“I Love China!” << endl; //實際上這個是主線程在執行,主線程從main()函數返回,則整個進程執行完畢。

主線程從main()開始執行,那麼我們自己創建的線程,也需要從一個函數開始運行(初始函數),一旦這個函數運行完畢,就代表着我們這個線程運行結束。

整個進程是否執行完畢的標誌是主線程是否執行完,如果主線程執行完畢了,就代表整個進程執行完畢了;
此時,一般情況下:如果其他子線程還沒有執行完畢,那麼這些子線程也會被操作系統強行終止。
所以,一般情況下,我們得到一個結論:如果大家想保持子線程的運行狀態的話,那麼大家要讓主線程一直保持運行,不要讓主線程停止。
(這條規則有例外)
a、包含一個頭文件thread要包含進來

void myprint(){
	cout<<"我的線程開始執行了"<<endl;
	//...
	cout<<"我的線程執行完畢了1"<<endl;
	cout<<"我的線程執行完畢了2"<<endl;
	cout<<"我的線程執行完畢了3"<<endl;
	cout<<"我的線程執行完畢了4"<<endl;
	cout<<"我的線程執行完畢了5"<<endl;
	cout<<"我的線程執行完畢了6"<<endl;
	cout<<"我的線程執行完畢了7"<<endl;
	cout<<"我的線程執行完畢了8"<<endl;
	cout<<"我的線程執行完畢了9"<<endl;
}

class TA
{
public:
	int &m_i;
	TA(int &i):m_i(i)	//轉換構造函數
	{
		cout<<"TA()構造函數被執行"<<endl;
	}
	TA(const TA &ta):m_i(ta.m_i)
	{
		cout<<"TA()的拷貝構造函數被執行"<<endl;
	}
	~TA()
	{
		cout<<"TA()析構函數被執行"<<endl;
	}
	void operator()()	//不能帶參數
	{
		//cout<<"我的線程operator開始執行了"<<endl;
		cout<<"m_i1的值爲:"<<m_i<<endl;
		cout<<"m_i2的值爲:"<<m_i<<endl;
		cout<<"m_i3的值爲:"<<m_i<<endl;
		cout<<"m_i4的值爲:"<<m_i<<endl;
		cout<<"m_i5的值爲:"<<m_i<<endl;
		cout<<"m_i6的值爲:"<<m_i<<endl;
	}
};

b、初始函數要寫。
c、main中開始寫代碼
大家必須明確一點:有兩個線程在跑,相當整個程序的執行有兩條線在同時走,所以可以同時幹兩件事,即使一條線被堵住了,另外一條線還是可以通行的。這就是多線程。

1.1、thread:是個標準庫裏的類

1.2、join():加入/匯合,說白了就是阻塞,阻塞主線程,讓主線程等待子線程執行完畢,然後子線程和主線程匯合,然後主線程再往下走

如果主線程執行完畢了,但子線程沒執行完畢,這種程序員是不合格的,寫出來的程序也是不穩定的;
一個書寫良好的程序,應該是主線程等待子線程執行完畢後,自己才能最終退出;

1.3、detach():傳統多線程程序主線程要等待子線程執行完畢,然後自己再最後退出;

detach:分離,也就是主線程不和子線程匯合了,你主線程執行你的,我子線程執行我的,你主線程也不必等我子線程運行結束,你可以先執行結束,這並不影響主線程執行。
爲什麼引入detach():我們創建了很多子線程,讓主線程逐個等待子線程結束,這種編程方法不太好,所以引入了detach();
一旦detach()之後,與這個主線程關聯的thread對象就會失去與這個主線程的關聯,此時這個子線程就會駐留在後臺運行(主線程跟該子線程失去聯繫)。
這個子線程就相當於被C++運行時刻接管,當這個子線程執行完成後,由運行時庫負責清理該線程相關的資源(守護線程)。
detach()使用線程myprint失去我們自己的控制。

1.4、joinable():判斷是否可以成功使用join()或者detach()的;返回true(可以join或者detach)或者false(不能join或者detach)。

myprint可調用對象。

thread mytobj(myprint);	//1、創建了線程,線程執行起點(入口)myprint();
//2、myprint線程開始執行。
if (mytobj.joinable())
{
	mytobj.join();
}

//阻塞主線程並等待myprint子線程執行完
mytobj.join();	//主線程阻塞到這裏等待myprint()執行完,當子線程執行完畢,這個join()就執行完畢,主線程就繼續往下走。
mytobj.detach();//一旦調用了detach(),就不能再用join(),否則系統會報告異常。
if (myobj.joinable())
{
	cout<<"joinable == true"<<endl;
}
else
{
	cout<<"joinable == false"<<endl;
}


cout<<"I Love China!1"<<endl;
cout<<"I Love China!2"<<endl;
cout<<"I Love China!3"<<endl;
cout<<"I Love China!4"<<endl;
cout<<"I Love China!5"<<endl;
return 0;

二:其他創建線程的手法

2.1、用類對象(可調用對象),以及一個問題範例

大家可能還有一個疑問:一旦調用了detach(),那我主線程執行結束了,我這裏用的這個ta這個對象還在嗎?
這個對象實際是被複制到線程中去;

int myi = 6;
TA ta(myi);
thread  mytobj3(ta);	//ta:可調用對象。
mytobj3.join();	//等待子線程執行結束
mytobj3.detach();	//使用時普通變量之類的傳引用要注意參數在主線程的生命週期,類對象thread也會自己拷貝臨時變量
cout<<"I Love China"<<endl;
return 0;

2.2、用lambda表達式

auto mylamthread = []
{
	cout<<"我的線程3開始執行了"<<endl;
	//...
	cout<<"我的線程3執行結束了"<<endl;
};
thread mytobj4(mylamthread);
mytobj4.join();

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