C++多線程之條件變量 - condition_variable


C++多線程之條件變量 - condition_variable


condition_variable是一個能夠堵塞調用線程直到被通知恢復的對象。

當調用condition_variable的某個等待函數時,它使用unique_lock來鎖定線程。該線程會保持堵塞狀態,直到被另一個線程通過調用同一個condition_variable對象的通知函數來喚醒為止。

1.wait(...)

void wait(unique_lock<mutex>& lck);

template<class Predicate>
void wait(unique_lock<mutex>& lck, Predicate pred())

調用wait函數后,當前線程的執行被堵塞,直到得到通知。
再阻塞線程時,該函數為自動調用lck.unlock(),從而允許其它被鎖定的線程能繼續執行。
當收到其它線程的通知時,該函數將取消阻塞並調用lck.lock(),使lck處於調用該函數時相同的狀態,然后函數返回(注意,最后的互斥鎖可能會在返回之前再次阻塞線程)。
通常通過另一個線程調用notify_one或notify_all來通知該函數喚醒,但是某些實現可能會產生虛假的調用喚醒,而不會調用這些函數中的任何一個,因此使用此功能時應該確保滿足其恢復條件,我們應該保證總是在循環中使用普通的wait函數。例如:while(...) cv.wait()

如果指定了pred,則該函數僅再pred返回false時菜阻塞,並且通知只能在線程變為true時才能取消阻塞線程,這對檢查虛假喚醒調用特別有用。等同於:while(!pred()) wait(lck)

另外需要特別注意的是在銷毀一個condition_variable時必須通知所有處於等待狀態的線程,否則它們可能會一直等待下去。
實例:

#include <iostream>
#include <thread>
#include <condition_variable>
#include <mutex>

std::mutex gMutex;
std::condition_variable gCv;

int gCargo;

bool sufficient()
{
    return 0 != gCargo;
}

void consume(int n)
{
    for(int i = 0; i < n; ++i)
    {
        std::unique_lock<std::mutex> lck(gMutex);
        gCv.wait(lck, sufficient);
        std::cout << "cargo: " << gCargo << std::endl;
        gCargo = 0;
    }
    
}

int main()
{
    std::thread consume_tread(consume, 10);
    for(int i = 0; i < 10; ++i)
    {
        while(sufficient())
        {
            std::this_thread::yield();
        }
        std::unique_lock<std::mutex> lck(gMutex);
        gCargo = i + 1;
        gCv.notify_one();
    }
    consume_tread.join();
    return 0;
}

2.wait_until(...)

template <class Clock, class Duration>
cv_status wait_until (unique_lock<mutex>& lck,
                      const chrono::time_point<Clock,Duration>& abs_time);

當前線程的執行被阻塞,直到被通知或直到abs_time為止(以先發生者為准)。

在阻塞線程時,該函數會自動調用lck.unlock(),從而允許其他鎖定的線程繼續執行。

收到通知或到達abs_time后,該函數將取消阻塞並調用lck.lock(),使lck處於與調用該函數相同的狀態。然后函數返回(注意,最后的互斥鎖可能會在返回之前再次阻塞線程)。

template <class Clock, class Duration, class Predicate>
bool wait_until (unique_lock<mutex>& lck,
                 const chrono::time_point<Clock,Duration>& abs_time,
                 Predicate pred);

如果指定了pred,等同於:

while (!pred())
  if ( wait_until(lck,abs_time) == cv_status::timeout)
    return pred();

3.wait_for(...)

template<class Rep, class Period>
cv_status wait_for(unique_lock<mutex>& lck,
                   const chrono::duration<Rep, Period>& rel_time);

等同於:

cv.wait_until(lck, steady_clock::now + rel_time)

示例:

// condition_variable::wait_for example
#include <iostream>           // std::cout
#include <thread>             // std::thread
#include <chrono>             // std::chrono::seconds
#include <mutex>              // std::mutex, std::unique_lock
#include <condition_variable> // std::condition_variable, std::cv_status

std::condition_variable cv;

int value;

void read_value() {
  std::cin >> value;
  cv.notify_one();
}

int main ()
{
  std::cout << "Please, enter an integer (I'll be printing dots): \n";
  std::thread th (read_value);

  std::mutex mtx;
  std::unique_lock<std::mutex> lck(mtx);
  while (cv.wait_for(lck,std::chrono::seconds(1))==std::cv_status::timeout) {
    std::cout << '.' << std::endl;
  }
  std::cout << "You entered: " << value << '\n';

  th.join();

  return 0;
}
template<class Rep, class Period, class Predicate>
bool wait_for(unique_lock<mutex>* lck, 
             const chrono::duration<Rep, Period>& rel_time,
             Predicate pred);

等同於:

cv.wait_until(lck, steady_clock::now() + rel_time, move(pred))

4.cv_status

enum class cv_status { no_timeout, timeout };

5.notify_one

void notify_one() noexcept;

從正在等待該條件變量的所有線程中解除一個線程的阻塞。如果沒有線程在等待,則該函數不執行任何操作。如果有多個線程在等待它也不會指定解除哪個線程的阻塞狀態。

6.notify_all

void notify_all() noexcept;

解除所有正在等待該條件變量線程的阻塞狀態。如果沒有線程在等待,則該函數不執行任何操作。

參考鏈接鏈接


免責聲明!

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



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