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;

解除所有正在等待该条件变量线程的阻塞状态。如果没有线程在等待,则该函数不执行任何操作。

参考链接链接

posted @ 2020-03-05 15:41  小肚哥  阅读(951)  评论(0编辑  收藏  举报