[C++11 多线程同步] --- 条件变量
Posted Overboom
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[C++11 多线程同步] --- 条件变量相关的知识,希望对你有一定的参考价值。
1 std::condition_variable 类介绍
当 std::condition_variable 对象的某个 wait 函数被调用的时候,它使用 std::unique_lock(通过 std::mutex) 来锁住当前线程。当前线程会一直被阻塞,直到另外一个线程在相同的 std::condition_variable 对象上调用了 notification 函数来唤醒当前线程。
std::condition_variable 对象通常使用 std::unique_lock< std::mutex> 来等待,如果需要使用另外的 lockable 类型,可以使用 std::condition_variable_any 类。
std::condition_variable和 std::condition_variable_any 类对比:
相同点:两者都能与std::mutex一起使用。
不同点:前者仅限于与 std::mutex 一起工作,而后者可以和任何满足最低标准的互斥量一起工作,从而加上了_any的后缀。condition_variable_any会产生额外的开销。
一般只推荐使用condition_variable。除非对灵活性有硬性要求,才会考虑condition_variable_any。
2 condition_variable member function介绍
2.1 构造函数
default (1) | condition_variable() |
copy [deleted] (2) | condition_variable (const condition_variable&) = delete; |
std::condition_variable 的拷贝构造函数被禁用,只提供了默认构造函数。
2.2 std::condition_variable::wait()
unconditional(1) | void wait (unique_lock& lck); |
---|---|
predicatate(2) | template void wait (unique_lock& lck, Predicate pred); |
std::condition_variable 提供了两种 wait() 函数。当前线程调用 wait() 后将被阻塞(此时当前线程应该获得了锁(mutex),不妨设获得锁 lck),直到另外某个线程调用 notify_* 唤醒了当前线程。
在线程被阻塞时,该函数会自动调用 lck.unlock() 释放锁,使得其他被阻塞在锁竞争上的线程得以继续执行。另外,一旦当前线程获得通知(notified,通常是另外某个线程调用 notify_* 唤醒了当前线程),wait() 函数也是自动调用 lck.lock(),使得 lck 的状态和 wait 函数被调用时相同。
代码段1:unconditional(1) wait方法的使用
#include <iostream> // std::cout
#include <thread> // std::thread
#include <mutex> // std::mutex, std::unique_lock
#include <condition_variable> // std::condition_variable
#include <windows.h>
std::mutex mtx; // 全局互斥锁.
std::condition_variable cv; // 全局条件变量.
bool ready = false; // 全局标志位.
void thread1()
std::cout << "thread1 start" << std::endl;
// step1:调用wait前,对当前线程加锁,达到mutex
std::unique_lock <std::mutex> lck(mtx);
std::cout << "before call wait" << std::endl;
cv.wait(lck);
// step2:调用wait后,线程会被阻塞
// step2: 同时系统自动调用mutex.unlock释放锁
// setp3: 一旦调用notify唤醒当前wait,系统也会主动调用mutex.lock(),使得mutex的状态跟刚调用wait时那样加锁
// 线程被唤醒, 继续往下执行打印
std::cout << "thread1 finished" << '\\n';
void thread2()
std::cout << "thread2 start" << std::endl;
std::unique_lock <std::mutex> lck(mtx);
std::cout << "before call notify, we sleep 15s" << std::endl;
std::cout << "you can oberserve in terminal, thread1 is blocking in wait" << std::endl;
Sleep(15000);
cv.notify_one(); // 唤醒线程.
int main()
std::thread th1(thread1);
std::thread th2(thread2);
th1.join();
th2.join();
system("pause");
return 0;
执行结果如下:
代码段2: predicatate(2) wait方法的使用
在第二种情况下(即设置了 Predicate),只有当 pred 条件为 false 时调用 wait() 才会阻塞当前线程,并且在收到其他线程的通知后只有当 pred 为 true 时才会被解除阻塞。因此第二种情况类似以下代码:
while (!pred())
wait(lck);
#include <iostream> // std::cout
#include <thread> // std::thread, std::this_thread::yield
#include <mutex> // std::mutex, std::unique_lock
#include <condition_variable> // std::condition_variable
std::mutex mtx;
std::condition_variable cv;
int cargo = 0;
bool shipment_available()
return cargo != 0;
// 消费者线程.
void consume(int n)
for (int i = 0; i < n; ++i)
std::unique_lock <std::mutex> lck(mtx);
cv.wait(lck, shipment_available);
std::cout << cargo << '\\n';
cargo = 0;
int main()
std::thread consumer_thread(consume, 10); // 消费者线程.
// 主线程为生产者线程, 生产 10 个物品.
for (int i = 0; i < 10; ++i)
while (shipment_available())
std::this_thread::yield();
std::unique_lock <std::mutex> lck(mtx);
cargo = i + 1;
cv.notify_one();
consumer_thread.join();
return 0;
执行结果如下:
2.3 std::condition_variable::wait_for / std::condition_variable::wait_until
与std::condition_variable::wait() 相似,只是 wait_for能够指定一个时间段,在当前线程收到通知或者指定的时间 rel_time 超时之前。该线程都会处于堵塞状态。而一旦超时或者收到了其它线程的通知,wait_for返回,剩下的处理步骤和 wait()相似。
与 std::condition_variable::wait_for 相似,可是wait_until能够指定一个时间点,在当前线程收到通知或者指定的时间点 abs_time超时之前,该线程都会处于堵塞状态。而一旦超时或者收到了其它线程的通知,wait_until返回。剩下的处理步骤和 wait_until() 相似。
2.4 std::condition_variable::notify_one() / std::condition_variable::notify_all()
otify_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;
运行结果如下:
3 std::condition_variable_any 介绍
与 std::condition_variable 类似,只不过 std::condition_variable_any 的 wait 函数可以接受任何 lockable 参数,而 std::condition_variable 只能接受 std::unique_lockstd::mutex 类型的参数,除此以外,和 std::condition_variable 几乎完全一样。
4 std::cv_status 枚举类型介绍
cv_status::timeout | wait_for 或者 wait_until 超时。 |
cv_status::no_timeout | wait_for 或者 wait_until 没有超时,即在规定的时间段内线程收到了通知。 |
5 std::notify_all_at_thread_exit介绍
函数原型为:
void notify_all_at_thread_exit (condition_variable& cond, unique_lock<mutex> lck);
当调用该函数的线程退出时,所有在 cond 条件变量上等待的线程都会收到通知。请看下例
#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 cv;
bool ready = false;
void print_id (int id)
std::unique_lock<std::mutex> lck(mtx);
while (!ready) cv.wait(lck);
// ...
std::cout << "thread " << id << '\\n';
void go()
std::unique_lock<std::mutex> lck(mtx);
std::notify_all_at_thread_exit(cv,std::move(lck));
ready = true;
int main ()
std::thread threads[10];
// spawn 10 threads:
for (int i=0; i<10; ++i)
threads[i] = std::thread(print_id,i);
std::cout << "10 threads ready to race...\\n";
std::thread(go).detach(); // go!
for (auto& th : threads) th.join();
return 0;
好了,到此为止,<condition_variable> 头文件中的两个条件变量类(std::condition_variable 和 std::condition_variable_any)、枚举类型(std::cv_status)、以及辅助函数(std::notify_all_at_thread_exit())都已经介绍完了
参看连接:
https://www.jianshu.com/p/a31d4fb5594f
https://www.cnblogs.com/wangshaowei/p/9593201.html
https://www.cnblogs.com/GuoXinxin/p/11675053.html
https://blog.csdn.net/c_base_jin/article/details/89741247
https://www.cnblogs.com/wangshaowei/p/9593201.html
以上是关于[C++11 多线程同步] --- 条件变量的主要内容,如果未能解决你的问题,请参考以下文章
[C++11 多线程同步] --- 条件变量的那些坑条件变量信号丢失和条件变量虚假唤醒(spurious wakeup)
[C++11 多线程同步] --- 条件变量的那些坑条件变量信号丢失和条件变量虚假唤醒(spurious wakeup)
[C++11 多线程同步] --- 条件变量的那些坑条件变量信号丢失和条件变量虚假唤醒(spurious wakeup)