1. 程式人生 > 實用技巧 >std::thread執行緒詳解

std::thread執行緒詳解

目錄

簡介

本文主要介紹了標準庫中的執行緒部分。執行緒是目前多核程式設計裡面最重要的一部分。
與程序程序相比,其所需的資源更少,執行緒之間溝通的方法更多; 他們之間的區別可以比較簡明用以下幾點概括[1]:

  1. 程序是資源分配的最小單位,執行緒是CPU排程的最小單位;也就是說程序之間的資源是相互隔離,而執行緒之間的是可以相互訪問的。
  2. 執行緒的存在依賴於程序,一個程序可以保護多個執行緒;
  3. 程序出現錯誤不會影響其他程序,但是一個執行緒出現錯誤,會影響同一程序下的所有執行緒。

執行緒的使用

執行緒的建立

一般使用std::thread建立一個執行緒。std::thread支援輸入一個函式物件,及一些引數,類似於std::bind,不過沒有佔位符。

最常見,最簡單的是對輸入一個匿名函式作為引數:

1
2
3
4
std::thread t1([]() {
  std::cout << "Hello World" << std::endl;
});
t1.join();

需要注意的是,在使用多執行緒的時候,如果使用類似於std::cout << "Hello World" << std::endl;的語句,容易造成輸出的混亂。比如

1
2
3
4
5
6
7
8
std::thread t1([]() {
  std::cout << "Hello World1" << std::endl;
});
std::thread t2([]() {
  std::cout << "Hello World2" << std::endl;
});
t1.join();
t2.join();

以上程式碼,我們一般來說期望的輸出是

但是,在一些情況下,它還會以以下的方法輸出

造成這個的原因很簡單,因為"Hello World"std::endl的輸出是分開的,所以他們之間可能被插入其他的輸出。為了解決這個問題。一般會使用一個完整的字串進行輸出,但是C++在格式化這一方面做的比較差(C++20

format庫看起來還不錯),所以一般情況下會使用printf輸出。

執行緒的方法和屬性

  1. joinable()判斷執行緒是否可連線(可執行執行緒)的,有以下情況的,為不可連線的:

    1. 構造時,thread()沒有引數;
    2. 該物件的執行緒已經被移動了;
    3. 該執行緒已經被joindetach
  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_stopjoin

除此之外,std::jthread還提供了一個內建的std::stop_token。可以通過執行緒函式的第一個引數來獲取(如果函式的第一個引數型別為std::stop_token)。

可以通過get_stop_sourceget_stop_tokenrequest_stop等方法來對其進行操作。

stop_token (C++20)

stop_token類似於一個訊號,告訴執行緒是否到了結束的時候。和stop_source一起使用。stop_token用來獲取是否退出(讀),而stop_source用來請求推出(讀寫)。其方法:

  1. request_stop請求退出

  2. stop_requested獲取是否已經請求退出

  3. stop_possible獲取是否可以請求退出

樣例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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;
}

輸出:

總結

本次講述了執行緒建立的一些方法,可以看到相比較C語言而言,由於C++11提出的函式物件(普通函式、匿名函式,std::bind的輸出等)使得執行緒的建立更加的方便。

下一次將講述執行緒之間的通訊。在C++中,執行緒之間的通訊方法和C語言提供的類似,不過是將其包裝了一下。