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/
转载文章受原作者版权保护。转载请注明原作者出处!