C++ std::thread join()的理解


在學習C++11的std::thread時,起初非常不理解join()函數的作用以及使用場景,官方的解釋又比較晦澀難懂,總覺得get不到關鍵點。看了很多文章后加上自己的理解,才覺得有了一點眉目,下面結合場景記錄一下自己的淺見。

 

在簡單的程序中一般只需要一個線程就可以搞定,也就是主線程:

int main()
{
    cout << "主線程開始運行\n";
}

現在假設我要做一個比較耗時的工作,從一個服務器下載一個視頻並進行處理,那么我的代碼會變成:

int main()
{
    cout << "主線程開始運行\n";
    download();  // 下載視頻到本地
    process();  // 本地處理
}

如果我需要兩個視頻素材一起在本地進行處理,也很簡單:

int main()
{
    cout << "主線程開始運行\n";
    download1();  // 下載視頻1
    download2();  // 下載視頻2
    process();  // 一起處理一下
}

本身這么做完全沒有問題,可是就是有點浪費時間,如果兩個視頻能夠同時下載就好了,這時候線程就派上了用場:

void download1()
{
    cout << "開始下載第一個視頻..." << endl;
    for (int i = 0; i < 100; ++i) {
        std::this_thread::sleep_for(std::chrono::milliseconds(50));
        cout << "下載進度:" << i << endl;
    }
    cout << "第一個視頻下載完成..." << endl;
}

void download2()
{
    cout << "開始下載第二個視頻..." << endl;
    for (int i = 0; i < 100; ++i) {
        std::this_thread::sleep_for(std::chrono::milliseconds(30));
        cout << "下載進度:" << i << endl;
    }
    cout << "第二個視頻下載完成..." << endl;
}

int main()
{
    cout << "主線程開始運行\n";
    std::thread d2(download2);
    download1();

    process();
}

主線程叫來了d2這個線程去下載第二個視頻,自己去下載第一個視頻,減輕了自己的工作量也縮短了時間,仔細看一下download2()中的sleep可以發現,兩個視頻同時下載肯定是視頻二先下載完,這樣在主線程下載完視頻一的時候視頻二已經准備好了,后面就可以一起進行處理,沒什么問題,但是萬一視頻二的下載時間比視頻一的時間長呢?當視頻一下載完了,d2還沒干完活,本地還沒有視頻二,接下來處理的時候肯定會有問題,或者說接下來不能直接進行處理,要等d2干完活后,主線程才能去處理兩個視頻。

在這種場景下就用到了join()這個函數。

 

先貼一下關於join()函數的解釋:

The function returns when the thread execution has completed.This synchronizes the moment this function returns with the completion of all the operations in the thread: This blocks the execution of the thread that calls this function until the function called on construction returns (if it hasn't yet).

總結理解一下就是兩個關鍵點:

  • 誰調用了這個函數?調用了這個函數的線程對象,一定要等這個線程對象的方法(在構造時傳入的方法)執行完畢后(或者理解為這個線程的活干完了!),這個join()函數才能得到返回。
  • 在什么線程環境下調用了這個函數?上面說了必須要等線程方法執行完畢后才能返回,那必然是阻塞調用線程的,也就是說如果一個線程對象在一個線程環境調用了這個函數,那么這個線程環境就會被阻塞,直到這個線程對象在構造時傳入的方法執行完畢后,才能繼續往下走,另外如果線程對象在調用join()函數之前,就已經做完了自己的事情(在構造時傳入的方法執行完畢),那么這個函數不會阻塞線程環境,線程環境正常執行。

接下來修改代碼並結合起來解釋一下:

 1 void download1()
 2 {
 3     cout << "開始下載第一個視頻..." << endl;
 4     for (int i = 0; i < 100; ++i) {
 5         std::this_thread::sleep_for(std::chrono::milliseconds(50));
 6         cout << "第一個視頻下載進度:" << i << endl;
 7     }
 8     cout << "第一個視頻下載完成..." << endl;
 9 }
10 
11 void download2()
12 {
13     cout << "開始下載第二個視頻..." << endl;
14     for (int i = 0; i < 100; ++i) {
15         std::this_thread::sleep_for(std::chrono::milliseconds(80));
16         cout << "第二個視頻下載進度:" << i << endl;
17     }
18     cout << "第二個視頻下載完成..." << endl;
19 }
20 void process()
21 {
22     cout << "開始處理兩個視頻" << endl;
23 }
24 
25 int main()
26 {
27     cout << "主線程開始運行\n";
28     std::thread d2(download2);
29     download1();
30     d2.join();
31     process();
32 }

現在下載視頻1需要5秒,下載視頻2需要8秒,當視頻1下載完成后要等待視頻2下載完成才能一起進行處理,為了實現這個目的我們在30行只加入了一行代碼d2.join()

在這個場景下,我們明確兩個事情:

  • 誰調用了join()函數?d2這個線程對象調用了join()函數,因此必須等待d2的下載任務結束了,d2.join()函數才能得到返回。
  • d2在哪個線程環境下調用了join()函數?d2是在主線程的環境下調用了join()函數,因此主線程要等待d2的線程工作做完,否則主線程將一直處於block狀態;這里不要搞混的是d2真正做的任務(下載)是在另一個線程做的,但是d2調用join()函數的動作是在主線程環境下做的。

 

以上是結合具體場景分析join()函數的使用方法和用途,為了便於理解有些啰嗦了,當然實際中join()應該會有很多的用法和學問,先了解一下作為入門鋪墊吧。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM