《探索C++多线程》:condition_variable源码(二)
来源:互联网 发布:淘宝皇冠买家号出售 编辑:程序博客网 时间:2024/06/06 05:07
在上一文章《探索C++多线程》:condition_variable源码(一)中,学习到了condition_variable的原理和一些使用方法,其实在头文件<condition_variable>中还有另外一类条件变量:condition_variable_any,也就是这篇文章要讲到的了。
从名称上看,condition_variable与condition_variable_any的差别就是any,那么这个any是怎样体现出来的呢?C++11给出了解答:
如果没有看明白,那么再来:
现在应该比较明了了吧,说白了就是:condition_variable对象的方法只能用unique_lock<mutex>类型的锁,而condition_variable_any的方法能使用任何类型的锁,除此之外,二者并无大的差别。
其实,在这篇文章中,我想解决两个问题:
问题一:为什么condition_variable要配合使用 锁 而不是 互斥量 呢?
答案:这么做的目的就是为了保证多线程的异常安全。
试想,有这么一个线程:
mutex mtx;void foo() { mtx.lock(); while (not_ready) {// ... cv.wait(mtx);} mtx.unlock();}
若该线程对信号量mtx上锁之后,就会继续往下执行,如果说在执行mtx.unlock()之前,发生了安全异常,那么该线程就无法执行解锁了,也就是说互斥量一直被上锁状态,而使得其他线程无法获得持有权。如果,想要在异常情况下保证线程安全,那么需要程序员在编写代码上将异常情况考虑进去(使用try/catch/throw等等),一旦发生异常便要进行解锁操作,这样徒劳增编程的复杂度和代码量。
那么,由于有lock_guard与unique_lock这两个利器,现在就比较好办了,所以现在代码改为:
mutex mtx;void foo() { unique_lock<mutex> lck(mtx); while (not_ready) {// ... cv.wait(mtx);}}
这样一来,代码也变得简洁了许多。不论程序员是忘记了解锁,还是线程发生了异常,unique_lock的析构函数都会自动解锁,能够保证线程的异常安全。
问题二:为什么condition_variable的 锁类型 不是 lock_guard 而是 unique_lock 呢?
答案:这不仅是为了安全考虑,更主要的是只有unique_lock才能做到。
我们试想这么一个例子:
mutex mtx;void foo() { lock_guard<mutex> lck(mtx); while (not_ready) {// ... cv.wait(mtx);}}跟unique_lock类似。若该线程持有了锁,那么将会继续执行后面的语句,我们现在来分析一下关于互斥量上锁的情况:lock_guard加锁、wait解锁、wait加锁、lock_guard析构解锁。
那么问题就来了:
1、由于lock_guard只能在构造函数中对互斥量mtx加锁,且在析构函数中将互斥量mtx解锁,那么这个互斥量在wait中是怎么加锁、解锁的呢?这就根本木有办法,所以lock_guard是行不通滴;
2、退一万步港,就算就算lock_guard能够在wait中上锁,解锁。如果wait解锁到wait加锁这期间发生了异常,那么此时互斥量是处于解锁的状态,而异常发生后局部变量lock_guard要析构,在析构的时候就会进行解锁,此时解锁就会出错,因为mtx身上已经没有锁了。也就是说lock_guard确实是解决了一方面的异常安全,但也带来了另外的异常安全。
我们倒回来,再想一下unique_lock为什么可以做到异常安全呢?
如果忘记了unique_lock的源码,建议回顾一下:《探索C++多线程》:mutex源码(二)。在这里我还是把它粘出来,unique_lock的析构函数:
~unique_lock() _NOEXCEPT { // 析构 if (_Owns)// 若互斥量在析构之前已经解锁了,由于存在_Owns标志,不再解锁,所以不会发生析构异常 _Pmtx->unlock();// 若没有,则正常析构 }再来对比看一下lock_guard的析构函数:
~lock_guard() _NOEXCEPT {// 析构函数,解锁_MyMutex.unlock();}这样就明了一些了吧。另外,由于unique_lock提供了更多灵活的方法供我们使用,而lock_guard不提供任何方法,这也是一个因素,当然最重要的还是安全啦。
大家可以参考:
1、《Effective C++》条款29:为“异常安全”而努力是值得的;
2、StackOverflow:C++11: why does std::condition_variable use std::unique_lock?
- 《探索C++多线程》:condition_variable源码(二)
- 《探索C++多线程》:condition_variable源码(一)
- C++ 11 多线程 (condition_variable)(二)
- 《探索C++多线程》:thread源码(二)
- 《探索C++多线程》:mutex源码(二)
- 《探索C++多线程》:future源码(二)
- netty源码探索(二)
- condition_variable与多线程,互斥锁
- 《探索C++多线程》:thread源码(一)
- 《探索C++多线程》:mutex源码(一)
- 《探索C++多线程》:future源码(一)
- C#:多线程编程探索
- c++11线程之条件变量condition_variable(二)
- netty5源码探索(二)----AbstractByteBuf
- C# 多线程(二)
- C/C+语言struct深层探索(二)
- C++11多线程(八):std::condition_variable 详解
- c++11多线程编程之condition_variable
- 实验 11 广播网络 OSPF 配置
- 十种排序算法总结(冒泡、插入、选择、希尔、归并、堆、快速,计数,桶,基数)
- webstorm支持es6与vue文件高亮
- 丑数。。。vector赋予空间,min函数
- 黑马程序员 十三、GUI
- 《探索C++多线程》:condition_variable源码(二)
- codeforces 798 D. Mike and distribution 二维贪心
- 冬日初雪
- 黑马程序员 十四、Eclipse 的使用、增强 for 循环、自动拆箱与装箱等
- 几款免费wordpress主题推荐
- 网站常用的五种布局方案
- Qt设置中文乱码问题
- 关于网站做多语言切换功能的最简单实现思路
- 文章标题