第二節 線程啟動、結束,建立線程多法、join,detach
![](https://img.laitimes.com/img/9ZDMuAjOiMmIsIjOiQnIsIiclRnblN2XjlGcjAzNfRHLGZkRGZkRfJ3bs92YsYTMfVmepNHL6VkeOhXTq1ENNpHW4Z0MMBjVtJWd0ckW65UbM5WOHJWa5kHT20ESjBjUIF2X0hXZ0xCMx81dvRWYoNHLrdEZwZ1Rh5WNXp1bwNjW1ZUba9VZwlHdssmch1mclRXY39CXldWYtlWPzNXZj9mcw1ycz9WL49zZuBnL3gTNxIjM1kDMzETNwAjMwIzLc52YucWbp5GZzNmLn9Gbi1yZtl2Lc9CX6MHc0RHaiojIsJye.png)
一、範例示範線程運作的開始
- 程式運作起來,生成一個程序,該程序所屬的主線程開始自動運作;當主線程從main()函數傳回,則整個程序執行完畢
- 主線程從main()開始執行,那麼我們自己建立的線程,也需要從一個函數開始運作(初始函數),一旦這個函數運作完畢,線程也結束運作
- 整個程序是否執行完畢的标志是:主線程是否執行完,如果主線程執行完畢了,就代表整個程序執行完畢了,此時如果其他子線程還沒有執行完,也會被強行終止【此條有例外,以後會解釋】
建立一個線程:
- 包含頭檔案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;
}
重要補充:
線程類參數是一個可調用對象。
一組可執行的語句稱為可調用對象,c++中的可調用對象可以是函數、函數指針、lambda表達式、bind建立的對象或者重載了函數調用運算符的類對象。
二、其他建立線程的方法
①建立一個類,并編寫圓括号重載函數,初始化一個該類的對象,把該對象作為線程入口位址
class Ta
{
public:
void operator()() //不能帶參數
{
cout << "我的線程開始運作" << endl;
//-------------
//-------------
cout << "我的線程運作完畢" << endl;
}
};
//main函數裡的:
Ta ta;
thread myThread(ta);
myThread.join();
②lambda表達式建立線程
//main函數中
auto lambdaThread = [] {
cout << "我的線程開始執行了" << endl;
//-------------
//-------------
cout << "我的線程開始執行了" << endl;
};
thread myThread(lambdaThread);
myThread.join();
③把某個類中的某個函數作為線程的入口位址
class Data_
{
public:
void GetMsg(){}
void SaveMsh(){}
};
//main函數裡
Data_ s;
//第一個&意思是取址,第二個&意思是引用,相當于std::ref(s)
//thread oneobj(&Data_::SaveMsh,s)傳值也是可以的
//在其他的構造函數中&obj是不會代表引用的,會被當成取位址
//調用方式:對象成員函數位址,類執行個體,[成員函數參數]
//第二個參數可以傳遞對象s,也可以傳遞引用std::ref(s)或&s
//傳遞s,會調用拷貝構造函數在子線程中生成一個新的對象
//傳遞&,子線程中還是用的原來的對象,是以就不能detach,因為主線程運作完畢會把該對象釋放掉
thread oneobj(&Data_::SaveMsh,&s);
thread twoobj(&Data_::GetMsg,&s);
oneobj.join();
twoobj.join();