C++11 condition_variable

一 condition_variable
  • condition_variable 类是同步原语,能用于阻塞一个线程,或同时阻塞多个线程,直至另一线程修改共享变量(条件)并通知 condition_variable 。
  • 常用于生产-消费场景。生产线程通过 notify_one 或者 notify_all 通知消费线程,消费线程有任务则消费,无任务时通过 wait、wait_for 或 wait_until 进行等待。
二 定义
  1. 定义

    // 头文件 <condition_variable>
    class condition_variable; (C++11)
    
  2. 构造

    condition_variable(); (1)(C++11)
    condition_variable(const condition_variable&) = delete; (2)(C++11) 
    
  • 不能复制构造。
  1. operator= 已删除

  2. 析构

    ~condition_variable(); (C++11)
    
  • 若已通知所有线程,调用析构函数才是安全的。
三 通知
  1. notify_one
  • 通知一个等待的线程

    void notify_one() noexcept; (C++11)
    
  1. notify_all
  • 通知所有等待的线程

    void notify_all() noexcept; (C++11)
    
四 等待
  1. wait
  • 阻塞当前线程,直到条件变量被唤醒

    void wait( std::unique_lock<std::mutex>& lock ); (1)(C++11)
    template< class Predicate >
    void wait( std::unique_lock<std::mutex>& lock, Predicate pred ); (2)(C++11)
    
  • 其中 (2) 相当于:

    while (!pred()) {
        wait(lock);
    }
    
  1. wait_until
  • 阻塞当前线程,直到条件变量被唤醒,或直到抵达指定时间点。

    template< class Clock, class Duration >
    std::cv_status wait_until( std::unique_lock<std::mutex>& lock,
         const std::chrono::time_point<Clock, Duration>& timeout_time ); (1)(C++11)
    template< class Clock, class Duration, class Pred >
    bool wait_until( std::unique_lock<std::mutex>& lock,
         const std::chrono::time_point<Clock, Duration>& timeout_time, Pred pred ); (2)(C++11)
    
  • 其中 (2) 相当于:

    while (!pred()) {
        if (wait_until(lock, timeout_time) == std::cv_status::timeout) {
            return pred();
        }
    }
    return true;
    
  • time_point 参见此前文章 C++11 time_point

  1. wait_for
  • 阻塞当前线程,直到条件变量被唤醒,或到指定时限时长后

    template< class Rep, class Period >
    std::cv_status wait_for( std::unique_lock<std::mutex>& lock,
                             const std::chrono::duration<Rep, Period>& rel_time); (1)(C++11)
    template< class Rep, class Period, class Predicate >
    bool wait_for( std::unique_lock<std::mutex>& lock,
                   const std::chrono::duration<Rep, Period>& rel_time, Predicate pred); (2)(C++11)
    
  • 其中 (2) 相当于:

    return wait_until(lock, std::chrono::steady_clock::now() + rel_time, std::move(pred));
    
  • duration 参见此前文章 C++11 std::duration

五 例子
  1. demo1 (cppreference)
#include <condition_variable>
#include <iostream>
#include <mutex>
#include <string>
#include <thread>

std::mutex m;
std::condition_variable cv;
std::string data;
bool ready = false;
bool processed = false;

void worker_thread() {
  // Wait until main() sends data
  std::unique_lock<std::mutex> lk(m);
  cv.wait(lk, [] { return ready; });

  // after the wait, we own the lock.
  std::cout << "Worker thread is processing data\n";
  data += " after processing";

  // Send data back to main()
  processed = true;
  std::cout << "Worker thread signals data processing completed\n";

  // Manual unlocking is done before notifying, to avoid waking up
  // the waiting thread only to block again (see notify_one for details)
  lk.unlock(); 
  cv.notify_one();
}

int main() {
  std::thread worker(worker_thread);

  data = "Example data";
  // send data to the worker thread
  {
    std::lock_guard<std::mutex> lk(m);
    ready = true;
    std::cout << "main() signals data ready for processing\n";
  }
  cv.notify_one();

  // wait for the worker
  {
    std::unique_lock<std::mutex> lk(m);
    cv.wait(lk, [] { return processed; });
  }
  std::cout << "Back in main(), data = " << data << '\n';

  worker.join();

  std::cin.get();
  return 0;
}
  • 结果:
main() signals data ready for processing
Worker thread is processing data
Worker thread signals data processing completed
Back in main(), data = Example data after processing
  1. demo2
#include <chrono>
#include <condition_variable>
#include <iostream>
#include <vector>
#include <atomic>

std::mutex task_mutex;
std::vector<std::function<void(int)>> task_vc;

std::mutex cv_mutex;
std::condition_variable cv;

std::mutex cout_mutex; // 避免输出信息错乱

std::atomic<int> exit_flag = 0;

using namespace std::chrono_literals;

void ProcessLoop( int num) {
  while (!exit_flag) {
    {
      // 消费
      std::lock_guard<std::mutex> guard(task_mutex);
      if (task_vc.size() > 0) {
        auto task = task_vc.front();
        task_vc.erase(task_vc.begin());
        task(num);
      }
    }

    {
      { // 仅展示用,消费一次后强制等待100ms
        std::lock_guard<std::mutex> guard(cout_mutex);
        std::cout << "thread " << num
                  << " : wait for 100ms." << std::endl;
      }

      std::unique_lock<std::mutex> lock(cv_mutex);
      cv.wait_for(lock, 100ms);
    }
  }
}

int main() {
  std::vector<std::thread> td_vc;
  for (int i = 0; i < 4; i++) {
    td_vc.push_back(std::thread(ProcessLoop, i));
  }
 
  for (int i = 0; i < 10; i++) {
    std::lock_guard<std::mutex> guard(task_mutex);
    // 生产
    task_vc.push_back([](int num) {
      std::lock_guard<std::mutex> guard(cout_mutex);
      std::cout << "thread " << num << " : hello" << std::endl;
    });
    
    // 通知消费
    std::unique_lock<std::mutex> lock(cv_mutex);
    cv.notify_one();
  }
 
  std::this_thread::sleep_for(500ms); // 等待消费线程执行完毕
  exit_flag.exchange(1);

  std::cin.get();
  return 0;
}
  • 结果
thread 0 : wait for 100ms.
thread 1 : wait for 100ms.
thread 2 : wait for 100ms.
thread 1 : hello
thread 1 : wait for 100ms.
thread 0 : hello
thread 0 : wait for 100ms.
thread 2 : hello
thread 2 : wait for 100ms.
thread 3 : hello
thread 3 : wait for 100ms.
thread 3 : hello
thread 3 : wait for 100ms.
thread 2 : hello
thread 2 : wait for 100ms.
thread 1 : hello
thread 1 : wait for 100ms.
thread 0 : hello
thread 0 : wait for 100ms.
thread 3 : hello
thread 3 : wait for 100ms.
thread 0 : hello
thread 0 : wait for 100ms.
thread 3 : wait for 100ms.
thread 2 : wait for 100ms.
thread 1 : wait for 100ms.
...
六 注意
  1. std::condition_variable 只可与 std::unique_lock< std::mutex > 一同使用;此限制在一些平台上允许最大效率。
  2. std::condition_variable_any 提供可与任何基本可锁定 (BasicLockable) 对象,例如 std::shared_lock 一同使用的条件变量。std::condition_variable_any 能与 std::shared_lock 一同使用,以在 std::shared_mutex 上以共享所有权模式等待。
  3. 所以如果锁是 std::unique_lock,使用std::condition_variable以获得更好性能。
七 参考
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值