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
 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 l_guard(lobj.m_mutex, std::adopt_lock);
 65             std::lock_guard 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 l_guard(lobj.m_mutex, std::adopt_lock);
 78             std::lock_guard 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 guard_data(m_data_mutex);
113         std::lock_guard guard_otherData(m_otherData_mutex);
114
115         //m_data and m_otherData do something...

116     }
117
118     void func2(){
119         std::lock_guard guard_data(m_data_mutex);
120         std::lock_guard 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

Original: https://www.cnblogs.com/azbane/p/15483838.html
Author: 我是张洪铭我是熊博士
Title: std::get<C++11多线程库~线程间共享数据>(10):使用互斥量保护共享数据(4)

原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/536207/

转载文章受原作者版权保护。转载请注明原作者出处!

(0)

大家都在看

  • c++智能指针

    跟comptr类似 明确定义AddRef和Release,然后定义与comptr类似的一个辅助类. 这里有2种方式 1.Release的时候引用计数为0的时候删除对象 2.定义一个…

    C++ 2023年5月29日
    094
  • C++11 正则表达式——基础知识介绍

    C++11开始支持正则表达式,使得处理文本更加简洁方便。C++11 支持六种正则表达式语法:ECMAScript, basic(POSIX Basic Regular Expres…

    C++ 2023年5月29日
    037
  • 【转载】C++中替代sprintf的std::ostringstream输出流详解

    一、简单介绍 ostringstream是C++的一个字符集操作模板类,定义在sstream.h头文件中。ostringstream类通常用于执行C风格的串流的输出操作,格式化字符…

    C++ 2023年5月29日
    045
  • Microsoft Visual C++ 2019 v14.28.29617

    Microsoft Visual C++ Redistributable(系统运行库,简称MSVC,VB/VC)是Windows操作系统应用程序的基础类型库组件。 Microsof…

    C++ 2023年5月29日
    091
  • 【转】c++ set的用法

    1、关于set C++ STL之所以得到广泛的赞誉,也被很多人使用,不只是提供了像vector,string,list等方便的容器,更重要的是STL封装了许多复杂的数据结构算法和大…

    C++ 2023年5月29日
    049
  • c++报错: No viable overloaded ‘=’ 临时变量与引用参数 const

    对于一个函数的参数为引用,如果想接收临时变量,就必须加上 const。 class A{ public: A(int aa) {} }; void f(A& a) { };…

    C++ 2023年5月29日
    050
  • C++ 有向图最短路径之Dijkstra算法

    摘自:https://blog.csdn.net/chuanzhouxiao/article/details/88831371 一、思路 1.Dijkstra算法 每次都是从起始顶…

    C++ 2023年5月29日
    093
  • C/C++中static,const,inline三种关键字的总结(参照网络)

    一、 关于staticstatic 是C++中很常用的修饰符,它被用来控制变量的存储方式和可见性,下面我将从 static 修饰符的产生原因、作用谈起,全面分析static 修饰符…

    C++ 2023年5月29日
    066
  • 【C++服务端技术】对象池

    代码没贴全,就少一个锁头文件,可以做设计参考 设计思想就是维护一个空闲链表,没有用的就重新申请,有的话就拿链表的头,使用完又还给空闲链表。 /* 一个分配固定大小内存的内存池,使用…

    C++ 2023年5月29日
    066
  • (转)C/C++中计算程序运行时间

    以前经常听人提起如何计算程序运行时间,给出一系列函数,当时没有注意,随便选了clock()最简单的方式进行计算。等到真正需要检测程序性能提升了多少,才发现这里面有很多要注意的地方。…

    C++ 2023年5月29日
    055
  • C++检测和定位内存泄漏

    1、首先需要宏定义一下new运算符 解释: new(a, b, c) T; 会被解释成一个函数调用operator new(sizeof(T), a, b, c)。这是C++就有的…

    C++ 2023年5月29日
    070
  • 从三个语言(C++,Java,C#)的几个性能测试案例来看性能优化

    随着时间的发展,现在的虚拟机技术越来越成熟了,在有些情况下,Java,.Net等虚拟机密集计算的性能已经和C++相仿,在个别情况下,甚至还要更加优秀。本文详细分析几个性能测试案例,…

    C++ 2023年5月29日
    075
  • 【转】C++智能指针的正确使用方式

    对象所有权 首先需要理清楚的概念就是对象所有权的概念。所有权在 rust 语言中非常严格,写 rust 的时候必须要清楚自己创建的每个对象的所有权。 但是 C++ 比较自由,似乎我…

    C++ 2023年5月29日
    054
  • C++ *和&

    概述 在c++中,当申明变量int *p 的时,表示p是一个储存地址的变量;比如int _p=0,表示p指向地址为00000000的地址单元。当申明指针p之后,再用_p表示p指向的…

    C++ 2023年5月29日
    056
  • 自己总结 C++ 代码规范

    1.编写原则,代码尽量简单,简洁,高效,自己写的代码让自己和别人容易看懂 2.命名: a. 类的成员变量加前缀 m_(表示 member)。 常量全用大写的字母,用下划线分割单词(…

    C++ 2023年5月29日
    054
  • C++11 之 override

    公有继承包含两部分:一是 “函数接口” (interface),二是 “函数实现” (implementation) 如 Shape…

    C++ 2023年5月29日
    046
亲爱的 Coder【最近整理,可免费获取】👉 最新必读书单  | 👏 面试题下载  | 🌎 免费的AI知识星球