c/c++ 多線程 一個線程等待某種事件發生

多線程 一個線程等待某種事件發生

背景:某個線程在可以完成其任務以前可能須要等待另外一個線程完成其任務。

例如:坐夜間列車,爲了可以不坐過站,

1,整夜保持清醒,可是這樣你就會很是累,不可以睡覺。

2,若是你知道幾點會到你要下車的站,就能夠提早定個鬧鐘,而後睡覺等待鬧鐘叫醒你,可是若是車中間有延誤,鬧鐘響了,可是還沒到你要下次的站;還有一種更惡劣的狀況就是,鬧鐘還沒響,可是列車已通過站了。

3,最好的辦法就是,快到站前,有我的能把你叫醒。

爲了可以達到上面場景3的效果,條件變量(Condition variable)就登場了。

對應上面的3個場景,請看下面的代碼。

場景1的代碼:

while(某個條件){//這個條件由另外一個線程來變動,因此就一直循環來檢查這個條件,CPU就得不到休息,浪費系統的性能
    
}

場景2的代碼:

std::unique_lock<std::mutex> lk(m);
while(某個條件){//這個條件由另外一個線程來變動,先睡眠一會,等待別的線程變動這個條件,CPU獲得了休息,節省了系統的性能
    lk.unlock();
    sleep(休眠必定的時間);
    lk.lock();
}
//缺點:沒法準確知道要休眠多長的時間。休眠時間過長就會致使響應過慢,休眠時間太短,醒來發現條件還沒被變動,還得繼續休眠。

場景3的代碼:

#include <iostream>
#include <mutex>
#include <queue>
#include <condition_variable>
#include <thread>
#include <unistd.h>//sleep

std::mutex mut;
std::queue<int> data_queue;//-------------------①
std::condition_variable data_cond;

void data_preparation_thread(){
  int data = 0;
  while(true){
    data++;
    std::lock_guard<std::mutex> lk(mut);
    data_queue.push(data);//-------------------②
    data_cond.notify_one();//-------------------③
    std::cout << "after  notify_one" << std::endl; 
    //std::this_thread::sleep_for(1000);
    sleep(1);
  }
}

void data_process_thread(){
  while(true){
    std::unique_lock<std::mutex> lk(mut);//-------------------④
    std::cout << "before wait" << std::endl; 
    data_cond.wait(lk, []{return !data_queue.empty();});//-------------------⑤
    std::cout << "after  wait" << std::endl;
    int data = data_queue.front();
    std::cout << data << std::endl;
    data_queue.pop();
    lk.unlock();//-------------------⑥
    //假設處理數據data的函數process要花費大量時間,因此提早解鎖
    //process(data);
  }
}
int main(){
  std::thread t1(data_preparation_thread);
  std::thread t2(data_process_thread);
  t1.join();
  t2.join();
}

1,有一個在多個線程間傳遞數據的隊列①,修改隊列前鎖定隊列,把數據壓入隊列②,壓入完成後通知等待它的線程,說:我已經把數據作好,大家可使用了③。

2,另外一個線程使用隊列前,先鎖定這個隊列④,注意是用std::unique_lock而不是std::lock_guard,理由後面說。

3,data_cond.wait(),檢查隊列裏是否有數據(用的是lambda函數,也能夠是普通函數),

  • 若是條件不知足(lambda函數返回false),wait解鎖這個互斥元,並將該線程置於阻塞狀態,繼續等待notify_onde()來喚醒它。
  • 若是條件知足(lambda函數返回true),wait繼續鎖定這個互斥元,執行wait後面的代碼。

這就是爲何使用std::unique_lock而不是std::lock_guard。等待中的線程必須解鎖互斥元,並在wait返回true的時候從新鎖定這個互斥元,std::lock_guard沒有這個功能。若是線程在等待期間不解鎖互斥元,把數據壓入隊列的線程就沒法鎖定這個互斥元,就沒法壓入數據,就沒法執行notify_one(),因此等待的線程就永遠處於等待狀態。。。ios

4,std::unique_lock另外的靈活性,假設獲得隊列裏的數據後,要作一個特別耗時的處理,作這個耗時的處理前就應該解鎖這個互斥元⑥,std::unique_lock提供了這個靈活性,而std::lock_guard沒有提供這個靈活性。

5,notify_one()後,另外一個wait的線程不是立刻就被喚醒!!!

github源代碼c++

編譯方法:git

g++ -g condition_vari-4.1.cpp -std=c++11  -pthread

c/c++ 學習互助QQ羣:877684253

本人微信:xiaoshitou5854

相關文章
相關標籤/搜索