ok!現(xiàn)在你有一個能與C++11標(biāo)準(zhǔn)兼容的編譯器。接下來呢?一個C++多線程程序是什么樣子呢?其實,它看上去和其他C++程序差不多,通常是變量、類以及函數(shù)的組合。唯一的區(qū)別在于某些函數(shù)可以并發(fā)運行,所以需要確保共享數(shù)據(jù)在并發(fā)訪問時是安全的,詳見第3章。當(dāng)然,為了并發(fā)地運行函數(shù),必須使用特定的函數(shù)以及對象來管理各個線程。
從一個經(jīng)典的例子開始:一個打印“Hello World.”的程序。一個非常簡單的在單線程中運行的Hello World程序如下所示,當(dāng)我們談到多線程時,它可以作為一個基準(zhǔn)。
```c++
int main() { std::cout << "Hello World\n"; }
這個程序所做的就是將“Hello World”寫進(jìn)標(biāo)準(zhǔn)輸出流。讓我們將它與下面清單所示的簡單的“Hello, Concurrent World”程序做個比較,它啟動了一個獨立的線程來顯示這個信息。
清單 1.1 一個簡單的Hello, Concurrent World程序:
void hello() //② { std::cout << "Hello Concurrent World\n"; } int main() { std::thread t(hello); //③ t.join(); //④ }
第一個區(qū)別是增加了`#include <thread>`①,標(biāo)準(zhǔn)C++庫中對多線程支持的聲明在新的頭文件中:管理線程的函數(shù)和類在`<thread>`中聲明,而保護(hù)共享數(shù)據(jù)的函數(shù)和類在其他頭文件中聲明。
其次,打印信息的代碼被移動到了一個獨立的函數(shù)中②。因為每個線程都必須具有一個*初始函數(shù)*(initial function),新線程的執(zhí)行從這里開始。對于應(yīng)用程序來說,初始線程是main(),但是對于其他線程,可以在`std::thread`對象的構(gòu)造函數(shù)中指定——本例中,被命名為t③的`std::thread`對象擁有新函數(shù)hello()作為其初始函數(shù)。
下一個區(qū)別:與直接寫入標(biāo)準(zhǔn)輸出或是從main()調(diào)用hello()不同,該程序啟動了一個全新的線程來實現(xiàn),將線程數(shù)量一分為二——初始線程始于main(),而新線程始于hello()。
新的線程啟動之后③,初始線程繼續(xù)執(zhí)行。如果它不等待新線程結(jié)束,它就將自顧自地繼續(xù)運行到main()的結(jié)束,從而結(jié)束程序——有可能發(fā)生在新線程運行之前。這就是為什么在④這里調(diào)用`join()`的原因——詳見第2章,這會導(dǎo)致調(diào)用線程(在main()中)等待與`std::thread`對象相關(guān)聯(lián)的線程,即這個例子中的t。
這看起來僅僅為了將一條信息寫入標(biāo)準(zhǔn)輸出而做了大量的工作,確實如此——正如上文1.2.3節(jié)所描述的,一般來說并不值得為了如此簡單的任務(wù)而使用多線程,尤其是在這期間初始線程并沒做什么。本書后面的內(nèi)容中,將通過實例來展示在哪些情景下使用多線程可以獲得收益。