[C++11 並發編程] 11 - 線程間同步 - 等待一個訊息或某種條件

來源:互聯網
上載者:User

上一節,我們瞭解了如何對線程之間的共用資源進行保護的方法。但是,有些時候,我們需要線上程之間進行同步操作。一個線程等待另一個線程完成某項工作後,再繼續自己的工作。比如,某個線程需要等待一個訊息,或者某個條件變成true。接下來幾節,我們會看到如何使用C++標準庫來做到線程間同步。

等待另一個線程完成的方法有如下幾種:

一個線程不停地查詢某個被mutex保護的貢獻資料區中的標誌的狀態。另一個線程在完成工作後,設定這個標誌。這種方法十分浪費資源,因為第一個線程需要不停的運行而佔用CPU。並且,當第一個線程鎖定mutex後,第二個線程即使完成了工作,也無法立即設定這個標誌,因為mutex被第一個線程鎖定了。 第二種方法是在每次檢查表之後,執行一定時間的休眠。

bool flag;std::mutex m;void wait_for_flag(){  std::unique_lock<std::mutex> lk(m);  while(!flag)  {    lk.unlock();  // 1 解鎖互斥量    std::this_thread::sleep_for(std::chrono::milliseconds(100));  // 2 休眠100ms    lk.lock();   // 3 再鎖互斥量  }}
             比如,上面得了例子,解鎖mutex後,先睡眠100ms,在再次鎖定mutex。這樣做,比第一種方法要好一點,但是睡眠時間長短很難確定。短了,就跟沒有休眠一樣,很浪費CPU資源。長了,可能這個線程等待的另一個線程已經完成了操作,這個線程都還在休眠。可能會導致類似掉幀之類的問題。
第三種方法是我們所推薦的,使用C++標準庫提供的機制-條件變數來實現等待操作。條件變數關聯到某種事件或者其它條件,一個或多個線程可以通過這個變數來等待條件的發生。某個線程在發現該條件滿足後,可以通知其它因等待該條件而掛起的線程,喚醒它們讓它們繼續執行。
C++標準庫提供了條件變數的兩種實現:std::condition_variable和std::condition_variable_any。它們都被聲明在<condition_variable>庫標頭檔中。它們都需要和std::mutex配合工作而達到同步操作的目的,而區別在於,前者必須和std::mutex一起工作,而後者和可以和任何滿足特定需求的類似於mutex的模組一起工作。std::condition_variable_any更通用,但它需要更多的記憶體,對作業系統的效能和資源也有更大的影響。大多數情況下,std::condition_variable就夠了。 下面是一個使用std::condition_variable來實現線程間同步的執行個體。data_preparation_thread負責準備資料,接收使用者的輸入,一旦收到資料,通知data_processing_thread來進行處理。當使用者輸入q時,程式退出。
#include <iostream>#include <mutex>#include <condition_variable>#include <thread>#include <queue>static bool more = true;bool more_data_to_prepare(){return more;}struct data_chunk{char m_data = 'q';data_chunk(char c) : m_data(c) {}};data_chunk prepare_data(){std::cout << "data_preparation_thread prepare_data"<< std::endl;char x = 'q';std::cin >> x;if (x == 'q'){more = false;}    return data_chunk(x);}void process(data_chunk& data){std::cout << "process data: " << data.m_data << std::endl;}bool is_last_chunk(data_chunk& data){if (data.m_data == 'q')    {    return true;}return false;}std::mutex mut;std::queue<data_chunk> data_queue;// 用於線程間通訊的隊列 std::condition_variable data_cond;void data_preparation_thread(){    while(more_data_to_prepare())    {    std::cout << "data_preparation_thread while" << std::endl;        data_chunk const data=prepare_data();        std::lock_guard<std::mutex> lk(mut);        // 資料準備好後,使用lock_guard來鎖定訊號量,將資料插入隊列之中         data_queue.push(data);        std::cout << "data_preparation_thread notify_one" << std::endl;        // 通過條件變數通知其它等待的線程         data_cond.notify_one();    }}void data_processing_thread(){    while(true)    {    std::cout << "data_processing_thread while" << std::endl;    // 使用unique_lock,因為我們需要在取得資料之後,處理資料之間,解鎖mutex         std::unique_lock<std::mutex> lk(mut);        std::cout << "data_processing_thread before wait" << std::endl;        // 等待條件滿足,unique_lock和Lambda函數,判斷資料隊列是否為空白         data_cond.wait(lk,[]{return !data_queue.empty();});        std::cout << "data_processing_thread pass wait" << std::endl;        data_chunk data=data_queue.front();        data_queue.pop();        // 處理資料需要較多時間,所以先解鎖mutex         lk.unlock();        std::cout << "data_processing_thread process data" << std::endl;        process(data);        if(is_last_chunk(data))            break;    }}int main(){std::cout << "main" << std::endl;    std::thread t1(data_preparation_thread);    std::thread t2(data_processing_thread);        t1.join();    t2.join();}
程式執行效果如下:輸入a,data_preparation_thread喚醒data_processing_thread,輸入q,data_processing_thread再次被data_preparation_thread喚醒,處理資料之後,程式結束。
maindata_preparation_thread whiledata_preparation_thread prepare_datadata_processing_thread whiledata_processing_thread before waitadata_preparation_thread notify_onedata_preparation_thread whiledata_preparation_thread prepare_datadata_processing_thread pass waitdata_processing_thread process dataprocess data: adata_processing_thread whiledata_processing_thread before waitqdata_preparation_thread notify_onedata_processing_thread pass waitdata_processing_thread process dataprocess data: q--------------------------------Process exited after 4.063 seconds with return value 0Press any key to continue . . .





聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.