1. 程式人生 > >std::thread執行緒詳解(1)

std::thread執行緒詳解(1)

## 目錄 - [目錄](#目錄) - [簡介](#簡介) - [執行緒的使用](#執行緒的使用) - [執行緒的建立](#執行緒的建立) - [執行緒的方法和屬性](#執行緒的方法和屬性) - [std::jthread (C++20)](#stdjthread-c20) - [stop_token (C++20)](#stop_token-c20) - [總結](#總結) - [Ref](#ref) ## 簡介 本文主要介紹了標準庫中的執行緒部分。執行緒是目前多核程式設計裡面最重要的一部分。 與程序程序相比,其所需的資源更少,執行緒之間溝通的方法更多; 他們之間的區別可以比較簡明用以下幾點概括[1]: 1. 程序是資源分配的最小單位,執行緒是CPU排程的最小單位;也就是說程序之間的資源是相互隔離,而執行緒之間的是可以相互訪問的。 2. 執行緒的存在依賴於程序,一個程序可以保護多個執行緒; 3. 程序出現錯誤不會影響其他程序,但是一個執行緒出現錯誤,會影響同一程序下的所有執行緒。 ## 執行緒的使用 ### 執行緒的建立 一般使用`std::thread`建立一個執行緒。`std::thread`支援輸入一個函式物件,及一些引數,類似於`std::bind`,不過沒有佔位符。 最常見,最簡單的是對輸入一個匿名函式作為引數: ```c++ std::thread t1([]() { std::cout << "Hello World" << std::endl; }); t1.join(); ``` 需要注意的是,在使用多執行緒的時候,如果使用類似於`std::cout << "Hello World" << std::endl;`的語句,容易造成輸出的混亂。比如 ```C++ std::thread t1([]() { std::cout << "Hello World1" << std::endl; }); std::thread t2([]() { std::cout << "Hello World2" << std::endl; }); t1.join(); t2.join(); ``` 以上程式碼,我們一般來說期望的輸出是 ![期望的輸出](https://img2020.cnblogs.com/blog/2105008/202101/2105008-20210101210755155-528460963.png) 但是,在一些情況下,它還會以以下的方法輸出 ![錯誤的輸出](https://img2020.cnblogs.com/blog/2105008/202101/2105008-20210101210755069-745182546.png) 造成這個的原因很簡單,因為`"Hello World"`和`std::endl`的輸出是分開的,所以他們之間可能被插入其他的輸出。為了解決這個問題。一般會使用一個完整的字串進行輸出,但是C++在格式化這一方面做的比較差(`C++20`的`format`庫看起來還不錯),所以一般情況下會使用`printf`輸出。 ### 執行緒的方法和屬性 1. `joinable()`判斷執行緒是否可連線(可執行執行緒)的,有以下情況的,為不可連線的: 1. 構造時,`thread()`沒有引數; 2. 該物件的執行緒已經被移動了; 3. 該執行緒已經被`join`或`detach`; 2. `get_id()` 返回執行緒的ID; 3. `native_handle()` 返回`POSIX`標準的執行緒物件; 4. `join()` 等待執行緒執行完成; 5. `detach()` 分離執行緒,分離後物件不再擁有執行緒。該執行緒結束後,會自動回收記憶體。(並不會開啟另一個程序); 6. `swap()` 交換物件的執行緒。 ### std::jthread (C++20) 除了常用的`std::thread`外,標準庫還存在著另一個可以建立執行緒的類,`std::jthread`。他們之間的差別比較明顯的就是,`std::jthread`會在解構的時候判斷執行緒是否還在執行`joinable`,如果還在執行則自動呼叫`request_stop`和`join`。 除此之外,`std::jthread`還提供了一個內建的`std::stop_token`。可以通過執行緒函式的第一個引數來獲取(如果函式的第一個引數型別為`std::stop_token`)。 可以通過`get_stop_source`、`get_stop_token`、`request_stop`等方法來對其進行操作。 ### stop_token (C++20) `stop_token`類似於一個訊號,告訴執行緒是否到了結束的時候。和`stop_source`一起使用。`stop_token`用來獲取是否退出(讀),而`stop_source`用來請求推出(讀寫)。其方法: 1. `request_stop` 請求退出 2. `stop_requested` 獲取是否已經請求退出 3. `stop_possible` 獲取是否可以請求退出 樣例: ```C++ void thread_func(std::stop_token token) { int data = 0; while (!token.stop_requested()) { printf("%d\n", data); data++; std::this_thread::sleep_for(1s); } printf("Exit\n"); } int main() { std::jthread mythread(thread_func); std::this_thread::sleep_for(4s); return 0; } ``` 輸出: ![jthread](https://img2020.cnblogs.com/blog/2105008/202101/2105008-20210101210755062-1194481525.png) ## 總結 本次講述了執行緒建立的一些方法,可以看到相比較C語言而言,由於C++11提出的函式物件(普通函式、匿名函式,`std::bind`的輸出等)使得執行緒的建立更加的方便。 下一次將講述執行緒之間的通訊。在C++中,執行緒之間的通訊方法和C語言提供的類似,不過是將其包裝了一下。 ## Ref [1] https://www.zhihu.com/question/