<condition_variable>是C++标准程序库中的一个头文件,定义了C++11标准中的一些用于并发编程时表示条件变量的类与方法等。

条件变量是并发程序设计中的一种控制结构。多个线程访问一个共享资源(或称临界区)时,不但需要用互斥锁实现独享访问以避免并发错误(称为竞争危害),在获得互斥锁进入临界区后还需要检验特定条件是否成立:

(1)、如果不满足该条件,拥有互斥锁的线程应该释放该互斥锁,把自身阻塞(block)并挂到(suspend)条件变量的线程队列中

(2)、如果满足该条件,拥有互斥锁的线程在临界区内访问共享资源,在退出临界区时通知(notify)在条件变量的线程队列中处于阻塞状态的线程,被通知的线程必须重新申请对该互斥锁加锁。

C++11的标准库中新增加的条件变量的实现,与pthread的实现语义完全一致。使用条件变量做并发控制时,某一时刻阻塞在一个条件变量上的各个线程应该在调用wait操作时指明同一个互斥锁,此时该条件变量与该互斥锁绑定;否则程序的行为未定义。条件变量必须与互斥锁配合使用,其理由是程序需要判定某个条件(condition或称predict)是否成立,该条件可以是任意复杂。

离开临界区的线程用notify操作解除阻塞(unblock)在条件变量上的各个线程时,按照公平性(fairness)这些线程应该有平等的获得互斥锁的机会,不应让某个线程始终难以获得互斥锁被饿死(starvation),并且比后来到临界区的其它线程更为优先(即基本上FIFO)。一种办法是调用了notify_all的线程保持互斥锁,直到所有从条件变量上解除阻塞的线程都已经挂起(suspend)到互斥锁上,然后发起了notify_all的线程再释放互斥锁。互斥锁上一般都有比较完善的阻塞线程调度算法,一般会按照线程优先级调度,相同优先级按照FIFO调度。

发起notify的线程不需要拥有互斥锁。即将离开临界区的线程是先释放互斥锁还是先notify操作解除在条件变量上挂起线程的阻塞?表面看两种顺序都可以。但一般建议是先notify操作,后对互斥锁解锁。因为这既有利于上述的公平性,同时还避免了相反顺序时可能的优先级倒置。这种先notify后解锁的做法是悲观的(pessimization),因为被通知(notified)线程将立即被阻塞,等待通知(notifying)线程释放互斥锁。很多实现(特别是pthreads的很多实现)为了避免这种”匆忙与等待”(hurry up and wait)情形,把在条件变量的线程队列上处于等待的被通知线程直接移到互斥锁的线程队列上,而不唤醒这些线程。

C++11中引入了条件变量,其相关内容均在<condition_variable>中。这里主要介绍std::condition_variable类。

条件变量std::condition_variable用于多线程之间的通信,它可以阻塞一个或同时阻塞多个线程。std::condition_variable需要与std::unique_lock配合使用。std::condition_variable效果上相当于包装了pthread库中的pthread_cond_*()系列的函数。

当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类的成员函数:

(1)、构造函数:仅支持默认构造函数,拷贝、赋值和移动(move)均是被禁用的。

(2)、wait:当前线程调用wait()后将被阻塞,直到另外某个线程调用notify_*唤醒当前线程;当线程被阻塞时,该函数会自动调用std::mutex的unlock()释放锁,使得其它被阻塞在锁竞争上的线程得以继续执行。一旦当前线程获得通知(notify,通常是另外某个线程调用notify_*唤醒了当前线程),wait()函数也是自动调用std::mutex的lock()。wait分为无条件被阻塞和带条件的被阻塞两种。

无条件被阻塞:调用该函数前,当前线程应该已经对unique_lock<mutex> lck完成了加锁。所有使用同一个条件变量的线程必须在wait函数中使用同一个unique_lock<mutex>。该wait函数内部会自动调用lck.unlock()对互斥锁解锁,使得其他被阻塞在互斥锁上的线程恢复执行。使用本函数被阻塞的当前线程在获得通知(notified,通过别的线程调用 notify_*系列的函数)而被唤醒后,wait()函数恢复执行并自动调用lck.lock()对互斥锁加锁。

带条件的被阻塞:wait函数设置了谓词(Predicate),只有当pred条件为false时调用该wait函数才会阻塞当前线程,并且在收到其它线程的通知后只有当pred为true时才会被解除阻塞。因此,等效于while (!pred())  wait(lck).

(3)、wait_for:与wait()类似,只是wait_for可以指定一个时间段,在当前线程收到通知或者指定的时间超时之前,该线程都会处于阻塞状态。而一旦超时或者收到了其它线程的通知,wait_for返回,剩下的步骤和wait类似。

(4)、wait_until:与wait_for类似,只是wait_until可以指定一个时间点,在当前线程收到通知或者指定的时间点超时之前,该线程都会处于阻塞状态。而一旦超时或者收到了其它线程的通知,wait_until返回,剩下的处理步骤和wait类似。

(5)、notify_all: 唤醒所有的wait线程,如果当前没有等待线程,则该函数什么也不做。

(6)、notify_one:唤醒某个wait线程,如果当前没有等待线程,则该函数什么也不做;如果同时存在多个等待线程,则唤醒某个线程是不确定的(unspecified)。

条件变化存在虚假唤醒的情况,因此在线程被唤醒后需要检查条件是否满足。无论是notify_one或notify_all都是类似于发出脉冲信号,如果对wait的调用发生在notify之后是不会被唤醒的,所以接收者在使用wait等待之前也需要检查条件是否满足。

std::condition_variable_any类与std::condition_variable用法一样,区别仅在于std::condition_variable_any的wait函数可以接受任何lockable参数,而std::condition_variable只能接受std::unique_lock<std::mutex>类型的参数。

std::notify_all_at_thread_exit函数:当调用该函数的线程退出时,所有在cond条件变量上等待的线程都会收到通知。

std::condition_variable:A condition variable is an object able to block the calling thread until notified to resume. It uses a unique_lock (over a mutex) to lock the thread when one of its wait functions is called. The thread remains blocked until woken up by another thread that calls a notification function on the same condition_variable object. Objects of type condition_variable always use unique_lock<mutex> to wait: for an alternative that works with any kind of lockable type, see condition_variable_any.

The condition_variable class is a synchronization primitive that can be used to block a thread, or multiple threads at the same time, until another thread both modifies a shared variable (the condition), and notifies the condition_variable.

The thread that intends to modify the variable has to:(1)、acquire a std::mutex (typically via std::lock_guard);(2)、perform the modification while the lock is held;(3)、execute notify_one or notify_all on the std::condition_variable (the lock does not need to be held for notification).

Any thread that intends to wait on std::condition_variable has to:(1)、acquire a std::unique_lock<std::mutex>, on the same mutex as used to protect the shared variable;(2)、execute wait, wait_for, or wait_until. The wait operations atomically release the mutex and suspend the execution of the thread;(3)、When the condition variable is notified, a timeout expires, or a spurious wake up occurs,the thread is awakened, and the mutex is atomically reacquired. The thread should then check the condition and resume waiting if the wake up was spurious.

std::condition_variable works only with std::unique_lock<std::mutex>; this restriction allows for maximal efficiency on some platforms. std::condition_variable_any provides a condition variable that works with any BasicLockable object, such as std::shared_lock.

下面是从其它文章中copy的std::condition_variable测试代码,详细内容介绍可以参考对应的reference:

  1. #include "condition_variable.hpp"
  2. #include <iostream>
  3. #include <chrono>
  4. #include <thread>
  5. #include <mutex>
  6. #include <condition_variable>
  7. #include <string>
  8. namespace condition_variable_ {
  9. //
  10. // reference: http://www.cplusplus.com/reference/condition_variable/condition_variable/
  11. std::mutex mtx;
  12. std::condition_variable cv;
  13. bool ready = false;
  14. static void print_id(int id)
  15. {
  16. std::unique_lock<std::mutex> lck(mtx);
  17. while (!ready) cv.wait(lck);
  18. // ...
  19. std::cout << "thread " << id << '\n';
  20. }
  21. static void go()
  22. {
  23. std::unique_lock<std::mutex> lck(mtx);
  24. ready = true;
  25. cv.notify_all();
  26. }
  27. int test_condition_variable_1()
  28. {
  29. std::thread threads[10];
  30. // spawn 10 threads:
  31. for (int i = 0; i<10; ++i)
  32. threads[i] = std::thread(print_id, i);
  33. std::cout << "10 threads ready to race...\n";
  34. go(); // go!
  35. for (auto& th : threads) th.join();
  36. return 0;
  37. }
  38. /
  39. // reference: http://www.cplusplus.com/reference/condition_variable/condition_variable/wait/
  40. // condition_variable::wait: Wait until notified,
  41. // The execution of the current thread (which shall have locked lck's mutex) is blocked until notified.
  42. // At the moment of blocking the thread, the function automatically calls lck.unlock(), allowing other locked threads to continue.
  43. // If pred is specified, the function only blocks if pred returns false,
  44. // and notifications can only unblock the thread when it becomes true (which is specially useful to check against spurious wake-up calls).
  45. std::mutex mtx2;
  46. std::condition_variable cv2;
  47. int cargo = 0;
  48. static bool shipment_available() { return cargo != 0; }
  49. static void consume(int n)
  50. {
  51. for (int i = 0; i<n; ++i) {
  52. std::unique_lock<std::mutex> lck(mtx2);
  53. cv2.wait(lck, shipment_available);
  54. // consume:
  55. std::cout << cargo << '\n';
  56. cargo = 0;
  57. std::cout << "****: " << cargo << std::endl;
  58. }
  59. }
  60. int test_condition_variable_wait()
  61. {
  62. std::thread consumer_thread(consume, 10);
  63. // produce 10 items when needed:
  64. for (int i = 0; i<10; ++i) {
  65. while (shipment_available()) std::this_thread::yield();
  66. std::unique_lock<std::mutex> lck(mtx2);
  67. cargo = i + 1;
  68. cv2.notify_one();
  69. }
  70. consumer_thread.join();
  71. return 0;
  72. }
  73. ///
  74. // reference: http://www.cplusplus.com/reference/condition_variable/condition_variable/wait_for/
  75. // condition_variable::wait_for: Wait for timeout or until notified
  76. // The execution of the current thread (which shall have locked lck's mutex) is blocked during rel_time,
  77. // or until notified (if the latter happens first).
  78. // At the moment of blocking the thread, the function automatically calls lck.unlock(),
  79. // allowing other locked threads to continue.
  80. std::condition_variable cv3;
  81. int value;
  82. static void read_value()
  83. {
  84. std::cin >> value;
  85. cv3.notify_one();
  86. }
  87. int test_condition_variable_wait_for()
  88. {
  89. std::cout << "Please, enter an integer (I'll be printing dots): \n";
  90. std::thread th(read_value);
  91. std::mutex mtx;
  92. std::unique_lock<std::mutex> lck(mtx);
  93. while (cv3.wait_for(lck, std::chrono::seconds(1)) == std::cv_status::timeout) {
  94. std::cout << '.' << std::endl;
  95. }
  96. std::cout << "You entered: " << value << '\n';
  97. th.join();
  98. return 0;
  99. }
  100. //
  101. // reference: http://www.cplusplus.com/reference/condition_variable/condition_variable/notify_one/
  102. // condition_variable::notify_one: Notify one, Unblocks one of the threads currently waiting for this condition.
  103. // If no threads are waiting, the function does nothing.
  104. // If more than one, it is unspecified which of the threads is selected.
  105. std::mutex mtx4;
  106. std::condition_variable produce4, consume4;
  107. int cargo4 = 0; // shared value by producers and consumers
  108. static void consumer4()
  109. {
  110. std::unique_lock<std::mutex> lck(mtx4);
  111. while (cargo4 == 0) consume4.wait(lck);
  112. std::cout << cargo4 << '\n';
  113. cargo4 = 0;
  114. produce4.notify_one();
  115. }
  116. static void producer(int id)
  117. {
  118. std::unique_lock<std::mutex> lck(mtx4);
  119. while (cargo4 != 0) produce4.wait(lck);
  120. cargo4 = id;
  121. consume4.notify_one();
  122. }
  123. int test_condition_variable_notify_one()
  124. {
  125. std::thread consumers[10], producers[10];
  126. // spawn 10 consumers and 10 producers:
  127. for (int i = 0; i<10; ++i) {
  128. consumers[i] = std::thread(consumer4);
  129. producers[i] = std::thread(producer, i + 1);
  130. }
  131. // join them back:
  132. for (int i = 0; i<10; ++i) {
  133. producers[i].join();
  134. consumers[i].join();
  135. }
  136. return 0;
  137. }
  138. /
  139. // reference: http://www.cplusplus.com/reference/condition_variable/condition_variable/notify_all/
  140. // condition_variable::notify_all: Notify all, Unblocks all threads currently waiting for this condition.
  141. // If no threads are waiting, the function does nothing.
  142. std::mutex mtx5;
  143. std::condition_variable cv5;
  144. bool ready5 = false;
  145. static void print_id5(int id) {
  146. std::unique_lock<std::mutex> lck(mtx5);
  147. while (!ready5) cv5.wait(lck);
  148. // ...
  149. std::cout << "thread " << id << '\n';
  150. }
  151. static void go5()
  152. {
  153. std::unique_lock<std::mutex> lck(mtx5);
  154. ready5 = true;
  155. cv5.notify_all();
  156. }
  157. int test_condition_variable_notify_all()
  158. {
  159. std::thread threads[10];
  160. // spawn 10 threads:
  161. for (int i = 0; i<10; ++i)
  162. threads[i] = std::thread(print_id5, i);
  163. std::cout << "10 threads ready to race...\n";
  164. go5(); // go!
  165. for (auto& th : threads) th.join();
  166. return 0;
  167. }
  168. // reference: http://en.cppreference.com/w/cpp/thread/condition_variable
  169. std::mutex m;
  170. std::condition_variable cv6;
  171. std::string data;
  172. bool ready6 = false;
  173. bool processed = false;
  174. static void worker_thread()
  175. {
  176. // Wait until main() sends data
  177. std::unique_lock<std::mutex> lk(m);
  178. cv6.wait(lk, []{return ready6; });
  179. // after the wait, we own the lock.
  180. std::cout << "Worker thread is processing data\n";
  181. data += " after processing";
  182. // Send data back to main()
  183. processed = true;
  184. std::cout << "Worker thread signals data processing completed\n";
  185. // Manual unlocking is done before notifying, to avoid waking up
  186. // the waiting thread only to block again (see notify_one for details)
  187. lk.unlock();
  188. cv6.notify_one();
  189. }
  190. int test_condition_variable_2()
  191. {
  192. std::thread worker(worker_thread);
  193. data = "Example data";
  194. // send data to the worker thread
  195. {
  196. std::lock_guard<std::mutex> lk(m);
  197. ready6 = true;
  198. std::cout << "main() signals data ready for processing\n";
  199. }
  200. cv6.notify_one();
  201. // wait for the worker
  202. {
  203. std::unique_lock<std::mutex> lk(m);
  204. cv6.wait(lk, []{return processed; });
  205. }
  206. std::cout << "Back in main(), data = " << data << '\n';
  207. worker.join();
  208. return 0;
  209. }
  210. } // namespace condition_variable_

GitHub: https://github.com/fengbingchun/Messy_Test

转载自:https://blog.csdn.net/fengbingchun/article/details/73695596

Multi-thread--C++11中std::condition_variable的使用相关推荐

  1. C++11中std::condition_variable的使用

    <condition_variable>是C++标准程序库中的一个头文件,定义了C++11标准中的一些用于并发编程时表示条件变量的类与方法等. 条件变量是并发程序设计中的一种控制结构.多个 ...

  2. C++/C++11中std::string用法汇总

    C++/C++11中std::string是个模板类,它是一个标准库.使用string类型必须首先包含<string>头文件.作为标准库的一部分,string定义在命名空间std中. st ...

  3. C++11中std::unique_lock的使用

    std::unique_lock为锁管理模板类,是对通用mutex的封装.std::unique_lock对象以独占所有权的方式(unique owership)管理mutex对象的上锁和解锁操作,即 ...

  4. Multi-thread--C++11中std::unique_lock的使用

    std::unique_lock为锁管理模板类,是对通用mutex的封装.std::unique_lock对象以独占所有权的方式(unique owership)管理mutex对象的上锁和解锁操作,即 ...

  5. C++11中std::async的使用

    C++11中的std::async是个模板函数.std::async异步调用函数,在某个时候以Args作为参数(可变长参数)调用Fn,无需等待Fn执行完成就可返回,返回结果是个std::future对 ...

  6. C++11中std::packaged_task的使用

    C++11中的std::packaged_task是个模板类.std::packaged_task包装任何可调用目标(函数.lambda表达式.bind表达式.函数对象)以便它可以被异步调用.它的返回 ...

  7. C++11中std::shared_future的使用

    C++11中的std::shared_future是个模板类.与std::future类似,std::shared_future提供了一种访问异步操作结果的机制:不同于std::future,std: ...

  8. C++11中std::future的使用

    C++11中的std::future是一个模板类.std::future提供了一种用于访问异步操作结果的机制.std::future所引用的共享状态不能与任何其它异步返回的对象共享(与std::sha ...

  9. 概率论中指数分布介绍及C++11中std::exponential_distribution的使用

    指数分布:在深度学习中,我们经常会需要一个在x=0点处取得边界点(sharp point)的分布.为了实现这一目的,我们可以使用指数分布(exponential distribution): p(x; ...

最新文章

  1. 自编码AutoEncoder 及PyTorch 实现
  2. 华硕笔记本装linux系统教程视频,在新买的华硕笔记本电脑安装linux手记
  3. 光纤中继器的防雷及日常维护方法介绍
  4. 听云数据库管理平台NetopGO简介
  5. c+命名空间_了解C ++中的命名空间
  6. 账号集中管理系统设计与实现----OpenLDAP
  7. WebStrom如何创建项目,为什么我开始选择HTML5到自定义路径报错
  8. HDU 2056 Rectangles
  9. 实战ReactNative 从入门到精通 重要技术解析,5分钟搭建iOS, Android App 实战一
  10. android studio 融云,融云 SDK 集成详解 – Android Studio
  11. 机器喵之红黑树(三)
  12. 黑苹果开启核显加速_「黑苹果」关于双显卡正确开启核显加速
  13. matlab condest,Matlab关于几个函数
  14. idc数据中心机房机柜收费标准
  15. 参加阿里的Java面试经验
  16. 学it需要学历吗_低学历者是否适合学IT?IT行业对学历要求高吗
  17. 中电智谷济南高铁西站新能源汽车充电站顺利开工
  18. java语言就业方向_java就业有哪些方向
  19. 网络安全——文件上传
  20. Pycharm取消下划线波浪线

热门文章

  1. 容器编排技术 -- Kubernetes kubectl create role 命令详解
  2. Linux oracle(常用命令)启动、停止、监听
  3. Sqlmap查找SQL注入漏洞入门
  4. C# 使用 WebBrowser 实现 HTML 转图片功能
  5. 解决后台json数据返回的字段需要替换的问题
  6. webpack -- 无法将“webpack”项识别为 cmdlet 。。。
  7. 中文站最好的WordPress主题推荐
  8. C#LeetCode刷题之#110-平衡二叉树(Balanced Binary Tree)
  9. 并发说明:如何构建多线程iOS应用
  10. 文件从头开始读函数_如何从头开始编写自己的Promisify函数