C++11 條件變量(condition_variable) 使用詳解


官網

一、總述

在C++11中,我們可以使用條件變量(condition_variable)實現多個線程間的同步操作;當條件不滿足時,相關線程被一直阻塞,直到某種條件出現,這些線程才會被喚醒。

主要成員函數如下:

 

二、具體函數:

 1、wait函數:

(1)wait(unique_lock <mutex>&lck)

當前線程的執行會被阻塞,直到收到 notify 為止。

(2)wait(unique_lock <mutex>&lck,Predicate pred)

當前線程僅在pred=false時阻塞;如果pred=true時,不阻塞。

wait()可依次拆分為三個操作:釋放互斥鎖等待在條件變量上再次獲取互斥鎖

2、notify_one:

notify_one():沒有參數、沒有返回值。

解除阻塞當前正在等待此條件的線程之一。如果沒有線程在等待,則還函數不執行任何操作。如果超過一個,不會指定具體哪一線程。

// condition_variable::notify_one
#include <iostream>           // std::cout
#include <thread>             // std::thread
#include <mutex>              // std::mutex, std::unique_lock
#include <condition_variable> // std::condition_variable

std::mutex mtx;
std::condition_variable produce,consume;

int cargo = 0;     // shared value by producers and consumers

void consumer () {
  std::unique_lock<std::mutex> lck(mtx);
  while (cargo==0) consume.wait(lck);
  std::cout << cargo << '\n';
  cargo=0;
  produce.notify_one();
}

void producer (int id) {
  std::unique_lock<std::mutex> lck(mtx);
  while (cargo!=0) produce.wait(lck);
  cargo = id;
  consume.notify_one();
}

int main ()
{
  std::thread consumers[10],producers[10];
  // spawn 10 consumers and 10 producers:
  for (int i=0; i<10; ++i) {
    consumers[i] = std::thread(consumer);
    producers[i] = std::thread(producer,i+1);
  }

  // join them back:
  for (int i=0; i<10; ++i) {
    producers[i].join();
    consumers[i].join();
  }

  return 0;
}

 

三、分析

條件變量是利用線程間共享的全局變量進行同步的一種機制,主要包括兩個動作:

(1)、一個線程因等待“條件變量的條件成立”而掛起;

(2)、另外一個線程使“條件成立”,給出信號,從而喚醒被等待的線程。

 

1、有什么用:

當需要死循環判斷某個條件成立與否時【true or false】,我們往往需要開一個線程死循環來判斷,這樣非常消耗CPU。使用條件變量,可以讓當前線程wait,釋放CPU,如果條件改變時,我們再notify退出線程,再次進行判斷。

2、其他解釋

想要修改共享變量(即“條件”)的線程必須:
(1). 獲得一個std::mutex
(2). 當持有鎖的時候,執行修改動作
(3). 對std::condition_variable執行notify_one或notify_all(當做notify動作時,不必持有鎖)

即使共享變量是原子性的,它也必須在mutex的保護下被修改,這是為了能夠將改動正確發布到正在等待的線程。

任意要等待std::condition_variable的線程必須:
(1). 獲取std::unique_lock<std::mutex>,這個mutex正是用來保護共享變量(即“條件”)的
(2). 執行wait, wait_for或者wait_until. 這些等待動作原子性地釋放mutex,並使得線程的執行暫停
(3). 當獲得條件變量的通知,或者超時,或者一個虛假的喚醒,那么線程就會被喚醒,並且獲得mutex. 然后線程應該檢查條件是否成立,如果是虛假喚醒,就繼續等待。

【注: 所謂虛假喚醒,就是因為某種未知的罕見的原因,線程被從等待狀態喚醒了,但其實共享變量(即條件)並未變為true。因此此時應繼續等待】

std::deque<int> q;
std::mutex mu;
std::condition_variable cond;

void function_1() //生產者
{
    int count = 10;
    while (count > 0) 
    {
        std::unique_lock<std::mutex> locker(mu);
        q.push_front(count);
        locker.unlock();
        cond.notify_one();  // Notify one waiting thread, if there is one.
        std::this_thread::sleep_for(std::chrono::seconds(1));
        count--;
    }
}

void function_2() //消費者
{
    int data = 0;
    while (data != 1) 
    {
        std::unique_lock<std::mutex> locker(mu);
        while (q.empty())
            cond.wait(locker); // Unlock mu and wait to be notified
        data = q.back();
        q.pop_back();
        locker.unlock();
        std::cout << "t2 got a value from t1: " << data << std::endl;
    }
}
int main() 
{
    std::thread t1(function_1);
    std::thread t2(function_2);
    t1.join();
    t2.join();
    return 0;
}

 

核心:

①、在消費者里判斷隊列是否為空后,如果不為空則wait,等待生產者發送notify信號

②、在生產者那里,如果生產了任務,則發送notify信號,告訴消費者可以試圖退出wait,判斷隊列是否為空,如果有任務則調度處理任務,如果還是空則說明此次notify是錯誤的,可能是其他地方發出來干擾的,生產者繼續wait。

③、流程:

軟件開啟,生成消費者線程消費隊列,應該是一個while循環,在循環里獲取鎖,再來一個while循環判斷條件,如果條件不成立則wait,wait會自動釋放鎖;

此時消費者已經沒有鎖了,在生產者線程里,獲取鎖,然后往里面加任務,退出作用域釋放鎖,然后notify告知消費者退出wait,消費者重新獲取鎖,然后從隊列里取任務;

整個過程,生產者加任務時生產者持有鎖,消費者取任務時消費者持有鎖。

 

資料:

https://www.cnblogs.com/judes/p/11132918.html

https://www.cnblogs.com/judes/p/11230628.html

 


免責聲明!

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



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