啟動(dòng)線程
使用C++線程庫啟動(dòng)線程可以歸結(jié)為構(gòu)造std::thread對(duì)象:
void do_some_work();
std::thread my_thread(do_some_work);
std::thread可以用可調(diào)用(callable)類型構(gòu)造舞萄,將帶有函數(shù)調(diào)用符類型的實(shí)力傳入std::thread類中,替換默認(rèn)的構(gòu)造函數(shù)舔示。
class back_ground
{
public:
void operator()() const
{
do_something();
do_something_else();
}
};
background_task f;
std::thread my_thread(f);
【注意】當(dāng)函數(shù)對(duì)象傳入到線程構(gòu)造函數(shù)中時(shí)借杰,要避免語法解析过吻,如果傳遞了一個(gè)臨時(shí)變量而不是一個(gè)明明的變量;C++編譯器會(huì)將其解析為函數(shù)聲明蔗衡,而不是類型對(duì)象的定義纤虽。
例如:
std::thread my_thread(background_task());
使用前面命名函數(shù)對(duì)象的方式,或使用多組括號(hào)绞惦,或使用新統(tǒng)一的初始化語法逼纸,或者使用lambda表達(dá)式,可以避免這個(gè)問題济蝉。如:
std::thread my_thread((background_task()));
std::thread my_thread{background_task()};
std::thread my_thread([]{
do_something();
do_something_else();
});
啟動(dòng)了線程需要明確是要等待線程結(jié)束杰刽,還是讓其自主運(yùn)行菠发。如果std::thread對(duì)象銷毀之前還沒有做出決定,程序就會(huì)終止贺嫂,因此即便有異常存在滓鸠,也需要確保線程能夠正確的加入(joined)或分離(detached)
2.1.2等待線程的完成
只能對(duì)一個(gè)線程使用一次join();一旦使用過join(),std::thread對(duì)象就不能再次加入,當(dāng)對(duì)其使用joinable()時(shí)涝婉,將返回否(false)哥力。
特殊情況下的等待
避免應(yīng)用被拋出的異常所終止,就需要做出一個(gè)決定墩弯。通常當(dāng)傾向于在無異常的情況下使用join()時(shí),需要在異常處理的過程中調(diào)用join()寞射,從而避免生命周期的問題渔工。
struct func;
void f()
{
int some_local_state = 0;
func my_func(some_local_state);
std::thread t(my_func);
try
{
do_something_in_current_thread();
}
catch(...)
{
t.join();
throw();
}
t.join();
}
如何確保線程在函數(shù)之前結(jié)束?
使用資源獲取即初始化方式(RAII桥温, Resource Acquisition Is Initialization)引矩,并且提供一個(gè)類,在析構(gòu)函數(shù)中使用join()
class thread_guard
{
std::thread& t;
public:
explicit thread_guard(std::thread& t_): t(t_) {}
~thread_guard()
{
if(t.joinable()) // 1
{
t.join(); // 2
}
}
thread_guard(thread_guard const&)=delete; // 3
thread_guard& operator=(thread_guard const&)=delete;
};
struct func;
void f()
{
int some_local_state=0;
func my_func(some_local_state);
std::thread t(my_func);
thread_guard g(t);
do_something_in_current_thread();
}
后臺(tái)運(yùn)行程序
對(duì)一個(gè)std::thread對(duì)象使用detach()就會(huì)將這個(gè)線程擱置在后臺(tái)運(yùn)行侵浸,這就意味著不能與這個(gè)線程產(chǎn)生直接交互旺韭。
因?yàn)镃++運(yùn)行庫保證,當(dāng)線程退出時(shí)掏觉,其相關(guān)資源的能夠正確的回收区端,所以后臺(tái)線程的歸屬和控制都會(huì)交給C++運(yùn)行庫處理。
分離線程的使用場景澳腹?
通常织盼, 稱分離線程為守護(hù)線程( daemon threads) 。 在UNIX中守護(hù)線程是指酱塔, 運(yùn)行在后臺(tái)沥邻,且沒有任何可用用戶接口的線程。 這種線程的特點(diǎn)就是長時(shí)間運(yùn)行羊娃;它們的生命周期可能會(huì)從某一個(gè)應(yīng)用起始到結(jié)束唐全, 也會(huì)在后臺(tái)執(zhí)行諸如監(jiān)事文件系統(tǒng)的任務(wù), 還有可能對(duì)未使用的緩存進(jìn)行清理蕊玷, 亦或?qū)?shù)據(jù)結(jié)構(gòu)進(jìn)行優(yōu)化邮利。 另一方面, 它也使用分離線程的另一種機(jī)制集畅, 確定線程什么時(shí)候結(jié)束近弟, 或者在“發(fā)后即忘”( fire and forget) 的任務(wù)在哪里使用到了這個(gè)線程。
使用條件
為了從一個(gè) std::thread 對(duì)象中分離一個(gè)線程( 前提是有一個(gè)可進(jìn)行分離的線程) :你不能對(duì)沒有執(zhí)行線程的 std::thread 對(duì)象使用detach()挺智。
void edit_document(std::string const& filename)
{
open_document_and_display_gui(filename);
while(!done_editing())
{
user_command cmd=get_user_input();
if(cmd.type==open_new_document)
{
std::string const new_name=get_filename_from_user();
std::thread t(edit_document,new_name); // 1
t.detach(); // 2
}
else
{
process_user_input(cmd);
}
}
}