• std::get<C++11多线程库~线程间共享数据>(10):使用互斥量保护共享数据(4)


      1 #ifndef DEADLOCK_QUESTIONDESCRIBLE_AND_SOLUTION_H
      2 #define DEADLOCK_QUESTIONDESCRIBLE_AND_SOLUTION_H
      3 
      4 /*
      5  * 话题1:使用互斥量保护共享数据
      6  *
      7  * 接下来学习第四个小话题:死锁,死锁的问题描述,死锁的解决方案
      8  *
      9  *      互斥量在解决共享数据安全的同时,除了会引入条件竞争, 还可能会引发死锁。
     10  *      条件竞争使得共享数据变得不安全,而死锁会导致各线程僵持,导致线程间出现互相等待的尴尬局面。
     11  *
     12  *      下面我们就来学习一下, 出现死锁的场景,以及如何避免死锁的发生。
     13  *
     14 */
     15 
     16 /*
     17  * 死锁的问题描述:
     18  *
     19  *      线程内出现两个或两个以上的互斥量时, 比如互斥量ma和mb, 有两个线程TA和TB, 线程TA先锁住了ma,准备继续对mb上锁, 而正在此时,
     20  * 线程TB锁住了mb,准备继续对ma上锁。 线程TA等待着线程TB释放互斥量mb,线程TB等待线程TA释放互斥量ma, 谁也不让谁,谁也不罢休,两个线程
     21  * 僵持等待。这就是死锁发生的情况。
     22 */
     23 
     24 
     25 /*
     26  * 死锁的解决方案:
     27  *
     28  *      一般的建议:让多个互斥量总是以相同的顺序上锁。互斥量mb总是在ma上锁之后上锁。可以将这个建议套入上面对死锁问题的描述,你就能够领悟了。
     29  *
     30  *      一般的建议往往只能搞定一般的问题, 如果多个互斥量所属于同一个作用域内,那么这个一般的建议是可以搞定的。那么多个互斥量时,如何能保证
     31  * 不出问题,一定不会出现死锁呢?
     32  *
     33  *      C++ 标准库提供了 std::lock, 可以一次性的锁住多个(两个及两个以上)互斥量,并且没有副作用(没有死锁风险)。std::lock的原则是,
     34  * 要么对所有互斥量都成功上锁,要么一个互斥量也不上锁。
     35 */
     36 
     37 #include <mutex>
     38 struct Data{
     39 
     40 };
     41 void swap(Data & ldata, Data &rdata);
     42 bool greater(Data &ldata, Data & rdata);
     43 
     44 class DeadLock_QuestionDescrible_and_Solution;
     45 void swap(DeadLock_QuestionDescrible_and_Solution &lobj, DeadLock_QuestionDescrible_and_Solution & robj);
     46 bool greater(DeadLock_QuestionDescrible_and_Solution &lobj, DeadLock_QuestionDescrible_and_Solution & robj);
     47 
     48 class DeadLock_QuestionDescrible_and_Solution
     49 {
     50     Data m_data;
     51     std::mutex m_mutex;
     52 public:
     53     DeadLock_QuestionDescrible_and_Solution(Data data):m_data(data){
     54 
     55     }
     56 
     57     friend void swap(DeadLock_QuestionDescrible_and_Solution &lobj, DeadLock_QuestionDescrible_and_Solution & robj)
     58     {
     59         if (&lobj == &robj){
     60             return ;
     61         }
     62         else{
     63             std::lock(lobj.m_mutex, robj.m_mutex);
     64             std::lock_guard<std::mutex> l_guard(lobj.m_mutex, std::adopt_lock);
     65             std::lock_guard<std::mutex> r_guard(robj.m_mutex, std::adopt_lock);
     66             swap(lobj.m_data, robj.m_data);
     67         }
     68     }
     69 
     70     friend bool greater(DeadLock_QuestionDescrible_and_Solution &lobj, DeadLock_QuestionDescrible_and_Solution & robj)
     71     {
     72         if (&lobj == &robj){
     73             return false;
     74         }
     75         else{
     76             std::lock(lobj.m_mutex, robj.m_mutex);
     77             std::lock_guard<std::mutex> l_guard(lobj.m_mutex, std::adopt_lock);
     78             std::lock_guard<std::mutex> r_guard(robj.m_mutex, std::adopt_lock);
     79             return greater(lobj.m_data, robj.m_data);
     80         }
     81     }
     82 };
     83 
     84 /*
     85  *      上边这个例子很好的阐明了, "一般的建议只能搞定一般的问题"。
     86  * 上边例子中 DeadLock_QuestionDescrible_and_Solution::swap 和 DeadLock_QuestionDescrible_and_Solution::greater 两个函数
     87  * 很好的说明了问题。
     88  *
     89  *      我们来分析一下, 每一个 Data 实例都有一个互斥量保护, 在 swap 和 greater 函数中,就都出现了 两个实例和两个互斥量。
     90  *
     91  *      咋一看,如果调用 swap 和 greater 函数时,参数按相同的顺序传递,似乎也不会出现死锁, 但实际上,我们把接口提供给用户,用户是很难保证
     92  * 多个参数按相同顺序调用我们提供的接口。
     93  *
     94  *      比如, 线程TA 调用 swap() 函数,按序传递 objA objB;线程TB 调用 greater()函数,按序传递 ojbB objA。 死锁就诞生了!
     95  *
     96  *      对于,一般建议提到的做法,按序对互斥量上锁, 下边给出一个例子。
     97  *
     98 */
     99 
    100 struct OtherData{
    101 
    102 };
    103 
    104 class OtherObject_ThreadSafe{
    105     Data m_data;
    106     std::mutex m_data_mutex;
    107 
    108     OtherData m_otherData;
    109     std::mutex m_otherData_mutex;
    110 
    111     void func1(){
    112         std::lock_guard<std::mutex> guard_data(m_data_mutex);
    113         std::lock_guard<std::mutex> guard_otherData(m_otherData_mutex);
    114 
    115         //m_data and m_otherData do something...
    116     }
    117 
    118     void func2(){
    119         std::lock_guard<std::mutex> guard_data(m_data_mutex);
    120         std::lock_guard<std::mutex> guard_otherData(m_otherData_mutex);
    121 
    122         //m_data and m_otherData do something...
    123     }
    124 };
    125 
    126 /*
    127  * 上边例子演示了,同一作用域内,需要对多个互斥量上锁,可以采用一般的建议,按相同的顺序对互斥量进行上锁,就可以搞定即保护了共享数据,又不会出现死锁。
    128  *
    129  *
    130  * 友情提醒: 实际编程中,能要底层级别手段搞定的问题,就尽量不要用高级的东西,毕竟越高级代价也就越高!!!
    131 */
    132 
    133 
    134 /*
    135  * 拓展:
    136  * C++17 对组合使用 std::lock()函数 和 std::lock_guard 模板类 提供了另一个支持,那便是: std::scoped_lock模板类
    137  *
    138  * 因此, 上边的 swap() 函数可以改写成如下形式。
    139 */
    140 
    141 friend void swap(DeadLock_QuestionDescrible_and_Solution &lobj, DeadLock_QuestionDescrible_and_Solution & robj)
    142 {
    143     if (&lobj == &robj){
    144         return ;
    145     }
    146     else{
    147         std::scoped_lock sl(lobj.m_mutex, robj.m_mutex);
    148         swap(lobj.m_data, robj.m_data);
    149     }
    150 }
    151 
    152 /*
    153  *      std::scoped_lock<>一种新的RAII类型模板类型,与std::lock_guard<>的功能等价,
    154  * 这个新类型能接受不定数量的互斥量类型作为模板参数,以及相应的互斥量(数量和类型)作为构造参数。
    155  * 互斥量支持构造即上锁,与std::lock的用法相同,其解锁阶段是在析构中进行。
    156 */
    157 #endif // DEADLOCK_QUESTIONDESCRIBLE_AND_SOLUTION_H
  • 相关阅读:
    Git:本地文件到远程仓库
    logstash.conf 配置:input kafka,filter,output elasticsearch/mysql
    PowerDesigner根据Excel设计数据表结构 Excel表结构导入PowerDesigner
    VBScript PowerDesigner使用手册
    PowerDesigner导出数据表结构到Excel 一个表一个Sheet 带链接目录
    PowerDesigner导出数据表结构到Excel 所有表结构在同一个Sheet中
    Windows下如何用virtualenv创建虚拟环境
    解决ValueError: day is out of range for month的问题
    正则表达式
    移动端库和框架
  • 原文地址:https://www.cnblogs.com/azbane/p/15483838.html
Copyright © 2020-2023  润新知