定義一個線程
#include <thread>
#include <chrono>
#include <iostream>
void doSomething()
{
while(true)
{
std::cout << "hello world" << std::endl;
std::this_thread::sleep_for(std::chrono::seconds(1));
}
}
int main()
{
std::thread t(doSomething); //①
if(t1.joinable())
{
t1.join(); //②
}
}
以上代碼簡單的頂一個線程,主線程阻塞等待子線程退出糙捺,子線程每個一秒打印一個"hello world". 代碼中① 聲明定義了一個線程弧关,我們往thread對象t中傳入一個函數(shù)指針,此后線程開始運行,該回調(diào)函數(shù)被調(diào)用碧注。代碼②調(diào)用了std::thtrad::join()函數(shù),該函數(shù)表示子線程與主線程的關(guān)系糖赔,主線程在此處將會等待子線程結(jié)束,并回收子線程的資源(一般一個子線程將會占用32M左右的內(nèi)存放典,如果無線開啟線程而不加以回收壳影,那么系統(tǒng)將會資源耗盡),需要注意的是掺栅,我們在調(diào)用std::thtrad::join()函數(shù)之前需要檢查這個線程是否是可以join的,否則會出現(xiàn)core_dump沙绝。
除了std::thtrad::join(),std::thread還提供了std::thtrad::detach()函數(shù)强饮,該函數(shù)表示主線程和子線程的關(guān)系是分離的行您,當(dāng)子線程結(jié)束時,其占用的內(nèi)存將自動釋放笛质。
另一個值得注意的是,如果在thread對象被銷毀時還沒有決定好線程銷毀的方式(join/detach), 那么在thread對象被銷毀時,析構(gòu)函數(shù)將會調(diào)用std::terminate()終止程序。
傳遞參數(shù)
上一節(jié)中我們的線程接收的函數(shù)指針沒有參數(shù)列表,那么如果有參數(shù)与学,或者參數(shù)還有const &, &&等限定符抑片,該怎么辦呢,參考以下代碼:
void doSomething(const int& num, std::string&& str)
{
while(true)
{
std::cout << str << std::endl;
std::this_thread::sleep_for(std::chrono::seconds(num));
}
}
int main()
{
int num = 1;
std::string str("hello world");
std::thread t1(doSomething, std::ref(num), std::move(str)); //①
if(t1.joinable())
{
t1.join(); //②
}
}
- 如果參數(shù)類型是一個拷貝焰枢,那直接傳遞蚓峦,沒有任何花里胡哨
- 如果參數(shù)類型是引用,那么可參考std::bind的語法济锄,需要使用std::ref將其聲明為一個引用
- 如果是右值應(yīng)用暑椰,那么需要使用std::move將其聲明為一個右值引用
移交線程歸屬權(quán)
STL庫中有許多對象是資源獨占型的,比如一個眾所周知的例子就是std::unique_ptr, 其實現(xiàn)原理其實就是荐绝,拷貝構(gòu)造函數(shù)和復(fù)制重載運算符被delete掉了一汽,例如:
unique_ptr(const unique_ptr&) = delete;
uuique_ptr& operator=(const unique_ptr&) = delete;
雖然std::thread是無法復(fù)制的,但是他是可以移動的(其實現(xiàn)依賴右值引用)低滩。因此我們可以寫下如下代碼:
int main()
{
std::thread t1([](){
while(1)
{
std::this_thread::sleep_for(std::chrono::seconds(1));
std::cout << "hello" << std::endl;
}
});
std::thread t2([](){
while(1)
{
std::this_thread::sleep_for(std::chrono::seconds(1));
std::cout << "world" << std::endl;
}
});
//t1.detach();
t1 = std::move(t2); //③
if(t1.joinable())
{
t1.join();
}
if(t2.joinable())
{
t2.join();
}
return 0;
}
以上代碼將會在③處出現(xiàn)錯誤召夹,std::terminate()將會被調(diào)用岩喷,因為t1重新接收了t2,而t1原來的實例將被釋放监憎,但卻沒指定其方式是join還是detach均驶。因此我們需要將③行之前的注釋解開。
相關(guān)函數(shù)
最大線程數(shù)
中所周知啊枫虏,如果在單核cpu上妇穴,所有的程序都是順序執(zhí)行的,但我們依然可以進行多線程操作隶债,這是因為系統(tǒng)在執(zhí)行過程中頻繁的從多個線程之間切換任務(wù)腾它,于是給用戶以程序并行的體驗,但是這個操作勢必會帶來更多的資源消耗死讹。標準庫中提供一個函數(shù)std::thread::hardware_concurrency()瞒滴,該函數(shù)返回系統(tǒng)最大支持線程數(shù)的一個指標,之所以叫他指標是因為如果資源數(shù)無法獲取赞警,那么函數(shù)將返回0妓忍。因此我們要注意盡信書不如無書。
線程表示符
可使用std::this_thread::get_id()得到當(dāng)前線程的id愧旦。
阻塞線程
可使用std::this_thread_sleep_for(std::chrono::second(1));使線程阻塞1s世剖,該操作會將線程掛起,因此不會對CUP造成過多負載笤虫。