一、簡單使用
C++11提供了一套精練的線程庫,小巧且易用。運行一個線程,可以直接創(chuàng)建一個std::thread的實例,線程在實例成功構(gòu)造成時啟動。若有底層平臺支持,成員函數(shù)std::thread::native_handle()將可提供對原生線程對象運行平臺特定的操作。
#include <thread>
#include <iostream>
void foo() {
std::cout << "Hello C++11" << std::endl;
}
int main() {
std::thread thread(foo); // 啟動線程foo
thread.join(); // 等待線程執(zhí)行完成
return 0;
}
編譯并運行,程序輸出:
Hello C++11
1、線程參數(shù)
當(dāng)需要向線程傳遞參數(shù)時,可以直接通過std::thread的構(gòu)造函數(shù)參數(shù)進行,構(gòu)造函數(shù)通過完美轉(zhuǎn)發(fā)將參數(shù)傳遞給線程函數(shù)。
#include <thread>
#include <iostream>
void hello(const char *name) {
std::cout << "Hello " << name << std::endl;
}
int main() {
std::thread thread(hello, "C++11");
thread.join();
return 0;
}
2. 類成員函數(shù)做為線程入口
類成員函數(shù)做為線程入口時,仍然十分簡單: 把this做為第一個參數(shù)傳遞進去即可。
#include <thread>
#include <iostream>
class Greet
{
const char *owner = "Greet";
public:
void SayHello(const char *name) {
std::cout << "Hello " << name << " from " << this->owner << std::endl;
}
};
int main() {
Greet greet;
std::thread thread(&Greet::SayHello, &greet, "C++11");
thread.join();
return 0;
}
//輸出:Hello C++11 from Greet
3. join: 等待線程執(zhí)行完成
線程如果像二哈似的撒手沒,則程序鐵定悲劇。因此std::thread提供了幾個線程管理的工具,其中join就是很重要的一個:等待線程執(zhí)行完成。即使當(dāng)線程函數(shù)已經(jīng)執(zhí)行完成后,調(diào)用join仍然是有效的。
4. 線程暫停
從外部讓線程暫停,會引發(fā)很多并發(fā)問題。大家可以百度一下,此處不做引申。這大概也是std::thread并沒有直接提供pause函數(shù)的原因。但有時線程在運行時,確實需要“停頓”一段時間怎么辦呢?可以使用std::this_thread::sleep_for或std::this_thread::sleep_until
#include <thread>
#include <iostream>
#include <chrono>
using namespace std::chrono;
void pausable() {
// sleep 500毫秒
std::this_thread::sleep_for(milliseconds(500));
// sleep 到指定時間點
std::this_thread::sleep_until(system_clock::now() + milliseconds(500));
}
int main() {
std::thread thread(pausable);
thread.join();
return 0;
}
5. 線程停止
一般情況下當(dāng)線程函數(shù)執(zhí)行完成后,線程“自然”停止。但在std::thread中有一種情況會造成線程異常終止,那就是:析構(gòu)。當(dāng)std::thread實例析構(gòu)時,如果線程還在運行,則線程會被強行終止掉,這可能會造成資源的泄漏,因此盡量在析構(gòu)前join一下,以確保線程成功結(jié)束。
如果確實想提前讓線程結(jié)束怎么辦呢?一個簡單的方法是使用“共享變量”,線程定期地去檢測該量,如果需要退出,則停止執(zhí)行,退出線程函數(shù)。使用“共享變量”需要注意,在多核、多CPU的情況下需要使用“原子”操作,關(guān)于原子操作后面會有專題講述。
二、進階(更多你可能需要知道的)
1. 拷貝
std::thread a(foo);
std::thread b;
b = a;
當(dāng)執(zhí)行以上代碼時,會發(fā)生什么?最終foo線程是由a管理,還是b來管理?答案是由b來管理。std::thread被設(shè)計為只能由一個實例來維護線程狀態(tài),以及對線程進行操作。因此當(dāng)發(fā)生賦值操作時,會發(fā)生線程所有權(quán)轉(zhuǎn)移。在macos下std::thread的賦值函數(shù)原型為:
thread& operator=(thread&& a);
賦完值后,原來由a管理的線程改為由b管理,b不再指向任何線程(相當(dāng)于執(zhí)行了detach操作)。如果b原本指向了一個線程,那么這個線程會被終止掉。
2. detach/joinable
detach是std::thread的成員函數(shù),函數(shù)原型為:
void detach();
bool joinable() const;
detach以后就失去了對線程的所有權(quán),不能再調(diào)用join了,因為線程已經(jīng)分離出去了,不再歸該實例管了。判斷線程是否還有對線程的所有權(quán)的一個簡單方式是調(diào)用joinable函數(shù),返回true則有,否則為無。
3. 線程內(nèi)部調(diào)用自身的join
自己等待自己執(zhí)行結(jié)束?如果程序員真這么干,那這個程序員一定是腦子短路了。對于這種行為C++11只能拋異常了。
三、其它
1. get_id
每個線程都有一個id,但此處的get_id與系統(tǒng)分配給線程的ID并不一是同一個東東。如果想取得系統(tǒng)分配的線程ID,可以調(diào)用native_handle函數(shù)。
2. 邏輯運算?
有些平臺下std::thread還支持若干邏輯運算,比如Visual C++, 但這并不是標(biāo)準(zhǔn)庫的行為,不要在跨平臺的場景中使用。
| 目錄 | 下一篇 C++11多線程-mutex |
|---|