C++11并發與多線程筆記(2) 線程啟動、結束,創建線程多法、join,detach
- 1. 范例演示線程運行的開始
- 1.1 創建一個線程:
- 1.2 join
- 1.3 datch
- 1.4 joinable
- 2. 其他創建線程的方法
- 2.1 用類 重載了函數調用運算符
- 2.2 lambda表達式創建線程
1. 范例演示線程運行的開始
- 程序運行起來,生成一個進程,該進程所屬的主線程開始自動運行;當主線程從main()函數返回,則整個進程執行完畢
- 主線程從**main()開始執行,**那么我們自己創建的線程,也需要從一個函數開始運行(初始函數),一旦這個函數運行完畢,線程也結束運行
- 整個進程是否執行完畢的標志是:主線程是否執行完,如果主線程執行完畢了,就代表整個進程執行完畢了,此時如果其他子線程還沒有執行完,也會被強行終止【此條有例外,以后會解釋】
1.1 創建一個線程:
- 包含頭文件thread
- 寫初始函數
- 在main中創建thread
必須要明白:有兩個線程在跑,相當于整個程序中有兩條線在同時走,即使一條被阻塞,另一條也能運行
#include <iostream>
#include <thread>
using namespace std;void myPrint()
{cout << "我的線程開始運行" << endl;//-------------//-------------cout << "我的線程運行完畢" << endl;return;
}int main()
{//(1)創建了線程,線程執行起點(入口)是myPrint;(2)執行線程thread myThread(myPrint);//(2)阻塞主線程并等待myPrint執行完,當myPrint執行完畢,join()就執行完畢,主線程繼續往下執行//join意為匯合,子線程和主線程回合myThread.join();//設置斷點可看到主線程等待子線程的過程//F11逐語句,就是每次執行一行語句,如果碰到函數調用,它就會進入到函數里面//F10逐過程,碰到函數時,不進入函數,把函數調用當成一條語句執行//(3)傳統多線程程序中,主線程要等待子線程執行完畢,然后自己才能向下執行//detach:分離,主線程不再與子線程匯合,不再等待子線程//detach后,子線程和主線程失去關聯,駐留在后臺,由C++運行時庫接管//myThread.detach();//(4)joinable()判斷是否可以成功使用join()或者detach()//如果返回true,證明可以調用join()或者detach()//如果返回false,證明調用過join()或者detach(),join()和detach()都不能再調用了if (myThread.joinable()){cout << "可以調用可以調用join()或者detach()" << endl;}else{cout << "不能調用可以調用join()或者detach()" << endl;}cout << "Hello World!" << endl;return 0;
}
1.2 join
join意為匯合,子線程和主線程回合
1.3 datch
detach:分離,主線程不再與子線程匯合,不再等待子線程
detach和join只能
1.4 joinable
joinable(): 判斷是否可以成功使用join()或者detach()。
如果返回true,證明可以調用join()或者detach()
如果返回false,證明調用過join()或者detach()
重要補充:
線程類參數是一個可調用對象。
一組可執行的語句稱為可調用對象,c++中的可調用對象可以是函數、函數指針、lambda表達式、bind創建的對象或者重載了函數調用運算符的類對象。
2. 其他創建線程的方法
2.1 用類 重載了函數調用運算符
創建一個類,并編寫圓括號重載函數,初始化一個該類的對象,把該對象作為線程入口地址
class Ta
{
public:void operator()() //不能帶參數{cout << "我的線程開始運行" << endl;//-------------//-------------cout << "我的線程運行完畢" << endl;}
};//main函數里的:
int main(){Ta ta;thread mytobj1(ta);//子進程mytobj1從ta開始執行mytobj1.join(); //等待子進程執行結束cout<<"I love China"<<endl;return 0;
}
問題案例(避免):
class TA{
public:int &m_i;//引用TA(int &i):m_i(i){}void operator() (){//不帶參數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;cout<<"m_i7的值為:"<<m_i<<endl;cout<<"m_i8的值為:"<<m_i<<endl;
}
int main(){int myi=6;TA ta(myi);thread mytobj2(ta);//ta:可調用對象mytobj2.detach();}
結果:
分析:傳入子線程的是引用,使用detach后,主線程提前結束,釋放myi,子線程的值就會無效。
-
一旦調用了detach(),那我主線程執行結束了,我這里用的這個ta這個對象還在嗎?〈對象不在)
-
這個對象實際上是被復制到線程中去(調用拷貝構造函數) ﹔執行完主球程后,ta會被銷毀,但是所復制的TA對象依舊存在。
-
所以,只要你這個TA類對象里沒有引用,沒有指針,那么就不會產生問題;
2.2 lambda表達式創建線程
int main(){auto mylamthrea=[]{cout << "我的線程開始運行" << endl;//-------------//-------------cout << "我的線程運行完畢" << endl;};thread myobj4(mylamthrea);myobj4.join();cout << "Hello World!" << endl;return 0;
}