@@ -91,7 +91,7 @@ void f() {
91
91
}
92
92
93
93
int main() {
94
- std::vector<std::thread>threads;
94
+ std::vector<std::thread> threads;
95
95
for (std::size_t i = 0; i < 10; ++i)
96
96
threads.emplace_back(f);
97
97
@@ -100,7 +100,7 @@ int main() {
100
100
}
101
101
```
102
102
103
- 这段代码你多次[ 运行] ( https://godbolt.org/z/K7hcYxec9 ) 它会得到毫无规律和格式的结果,我们可以使用[ 互斥量] ( https://zh.cppreference.com/w/cpp/thread/mutex ) 解决这个问题:
103
+ 这段代码你多次[ 运行] ( https://godbolt.org/z/93vTzcPzK ) 它会得到毫无规律和格式的结果,我们可以使用[ 互斥量] ( https://zh.cppreference.com/w/cpp/thread/mutex ) 解决这个问题:
104
104
105
105
``` cpp
106
106
#include < mutex> // 必要标头
@@ -182,7 +182,7 @@ private:
182
182
void f(){
183
183
//code..
184
184
{
185
- std::lock_guard<std::mutex>lc{ m };
185
+ std::lock_guard<std::mutex> lc{ m };
186
186
// 涉及共享资源的修改的代码...
187
187
}
188
188
//code..
@@ -206,12 +206,12 @@ void add_to_list(int n, std::list<int>& list) {
206
206
int sum = std::accumulate(numbers.begin(), numbers.end(), 0);
207
207
208
208
{
209
- std::lock_guard<std::mutex>lc{ m };
209
+ std::lock_guard<std::mutex> lc{ m };
210
210
list.push_back(sum);
211
211
}
212
212
}
213
213
void print_list(const std::list<int >& list){
214
- std::lock_guard< std::mutex > lc{ m };
214
+ std::lock_guard< std::mutex > lc{ m };
215
215
for(const auto& i : list){
216
216
std::cout << i << ' ';
217
217
}
@@ -231,7 +231,7 @@ t3.join();
231
231
t4.join();
232
232
```
233
233
234
- > 完整[ 代码测试] ( https://godbolt.org/z/ETeEsKhzK ) 。
234
+ > 完整[ 代码测试] ( https://godbolt.org/z/E3396dMxG ) 。
235
235
236
236
先看 ` add_to_list ` ,只有 ` list.push_back(sum) ` 涉及到了对共享数据的修改,需要进行保护,我们用 ` {} ` 包起来了。
237
237
@@ -326,7 +326,7 @@ class Data_wrapper{
326
326
public:
327
327
template<class Func >
328
328
void process_data(Func func){
329
- std::lock_guard< std::mutex > lc{m};
329
+ std::lock_guard< std::mutex > lc{m};
330
330
func(data); // 受保护数据传递给函数
331
331
}
332
332
};
@@ -368,20 +368,20 @@ std::mutex m1,m2;
368
368
std::size_t n{};
369
369
370
370
void f(){
371
- std::lock_guard<std::mutex>lc1{ m1 };
372
- std::lock_guard<std::mutex>lc2{ m2 };;
371
+ std::lock_guard<std::mutex> lc1{ m1 };
372
+ std::lock_guard<std::mutex> lc2{ m2 };;
373
373
++n;
374
374
}
375
375
void f2() {
376
- std::lock_guard<std::mutex>lc1{ m2 };
377
- std::lock_guard<std::mutex>lc2{ m1 };
376
+ std::lock_guard<std::mutex> lc1{ m2 };
377
+ std::lock_guard<std::mutex> lc2{ m1 };
378
378
++n;
379
379
}
380
380
```
381
381
382
382
` f ` 与 ` f2 ` 因为互斥量** 上锁顺序不同** ,就有死锁风险。函数 ` f ` 先锁定 ` m1 ` ,然后再尝试锁定 ` m2 ` ,而函数 ` f2 ` 先锁定 ` m2 ` 再锁定 ` m1 ` 。如果两个线程同时运行,它们就可能会彼此等待对方释放其所需的锁,从而造成死锁。
383
383
384
- > 简而言之,有可能函数 f 锁定了 m1,函数 f2 锁定了 m2,函数 f 要往下执行,给 m2 上锁,所以在等待 f2 解锁 m2,然而函数 f2 也在等待函数 f 解锁 m1 它才能往下执行。所以死锁。[ 测试代码] ( https://godbolt.org/z/b9zYs44of ) 。
384
+ > 简而言之,有可能函数 f 锁定了 m1,函数 f2 锁定了 m2,函数 f 要往下执行,给 m2 上锁,所以在等待 f2 解锁 m2,然而函数 f2 也在等待函数 f 解锁 m1 它才能往下执行。所以死锁。[ 测试代码] ( https://godbolt.org/z/T8vWYzqnT ) 。
385
385
386
386
---
387
387
@@ -399,8 +399,8 @@ private:
399
399
400
400
void swap(X& lhs, X& rhs) {
401
401
if (&lhs == &rhs) return;
402
- std::lock_guard< std::mutex > lock1{ lhs.m };
403
- std::lock_guard< std::mutex > lock2{ rhs.m };
402
+ std::lock_guard< std::mutex > lock1{ lhs.m };
403
+ std::lock_guard< std::mutex > lock2{ rhs.m };
404
404
swap(lhs.object, rhs.object);
405
405
}
406
406
```
@@ -417,7 +417,7 @@ std::thread t2{ [&] {swap(b, a); } }; // 2
417
417
418
418
` 2 ` 执行的时候,先上锁 b 的互斥量,再上锁 a 的互斥量。
419
419
420
- > 完全可能线程 A 执行 1 的时候上锁了 a 的互斥量,线程 B 执行 ` 2 ` 上锁了 b 的互斥量。线程 A 往下执行需要上锁 b 的互斥量,线程 B 则要上锁 a 的互斥量执行完毕才能解锁,哪个都没办法往下执行,** 死锁** 。[ 测试代码] ( https://godbolt.org/z/eYbjqEx54 ) 。
420
+ > 完全可能线程 A 执行 1 的时候上锁了 a 的互斥量,线程 B 执行 ` 2 ` 上锁了 b 的互斥量。线程 A 往下执行需要上锁 b 的互斥量,线程 B 则要上锁 a 的互斥量执行完毕才能解锁,哪个都没办法往下执行,** 死锁** 。[ 测试代码] ( https://godbolt.org/z/M6hfeb778 ) 。
421
421
422
422
其实也就是回到了第一个示例的问题。
423
423
@@ -427,8 +427,8 @@ C++ 标准库有很多办法解决这个问题,**可以使用 [`std::lock`](ht
427
427
void swap (X& lhs, X& rhs) {
428
428
if (&lhs == &rhs) return;
429
429
std::lock(lhs.m, rhs.m); // 给两个互斥量上锁
430
- std::lock_guard< std::mutex > lock1{ lhs.m,std::adopt_lock };
431
- std::lock_guard< std::mutex > lock2{ rhs.m,std::adopt_lock };
430
+ std::lock_guard< std::mutex > lock1{ lhs.m,std::adopt_lock };
431
+ std::lock_guard< std::mutex > lock2{ rhs.m,std::adopt_lock };
432
432
swap(lhs.object, rhs.object);
433
433
}
434
434
```
@@ -476,8 +476,8 @@ void swap(X& lhs, X& rhs) {
476
476
``` cpp
477
477
void swap (X& lhs, X& rhs) {
478
478
if (&lhs == &rhs) return;
479
- std::unique_lock< std::mutex > lock1{ lhs.m, std::defer_lock };
480
- std::unique_lock< std::mutex > lock2{ rhs.m, std::defer_lock };
479
+ std::unique_lock< std::mutex > lock1{ lhs.m, std::defer_lock };
480
+ std::unique_lock< std::mutex > lock2{ rhs.m, std::defer_lock };
481
481
std::lock(lock1, lock2);
482
482
swap(lhs.object, rhs.object);
483
483
++n;
@@ -563,7 +563,7 @@ std::mutex m;
563
563
564
564
int main () {
565
565
m.lock();
566
- std::unique_lock<std::mutex>lock{ m,std::adopt_lock };
566
+ std::unique_lock<std::mutex>lock { m,std::adopt_lock };
567
567
}
568
568
```
569
569
@@ -583,7 +583,7 @@ int main() {
583
583
void f () {
584
584
//code..
585
585
586
- std::unique_lock<std::mutex>lock{ m };
586
+ std::unique_lock<std::mutex> lock{ m };
587
587
588
588
// 涉及共享资源的修改的代码...
589
589
@@ -621,12 +621,12 @@ void f() {
621
621
```cpp
622
622
std::unique_lock<std::mutex>get_lock(){
623
623
extern std::mutex some_mutex;
624
- std::unique_lock<std::mutex>lk{ some_mutex };
624
+ std::unique_lock<std::mutex> lk{ some_mutex };
625
625
return lk;
626
626
627
627
}
628
628
void process_data(){
629
- std::unique_lock<std::mutex>lk{ get_lock() };
629
+ std::unique_lock<std::mutex> lk{ get_lock() };
630
630
// 执行一些任务...
631
631
}
632
632
```
@@ -639,7 +639,7 @@ void process_data(){
639
639
640
640
如果你简单写一个 ` std::mutex some_mutex ` 那么函数 ` process_data ` 中的 ` lk ` 会持有一个悬垂指针。
641
641
642
- > 举一个使用 ` extern std::mutex ` 的完整[ 运行示例] ( https://godbolt.org/z/bWv5fcdGf ) 。当然,其实理论上你 ` new std::mutex ` 也是完全可行...... 🤣🤣
642
+ > 举一个使用 ` extern std::mutex ` 的完整[ 运行示例] ( https://godbolt.org/z/z47x1Es5z ) 。当然,其实理论上你 ` new std::mutex ` 也是完全可行...... 🤣🤣
643
643
644
644
` std::unique_lock ` 是灵活的,同样允许在对象销毁之前就解锁互斥量,调用 ` unlock() ` 成员函数即可,不再强调。
645
645
@@ -654,7 +654,7 @@ void process_data(){
654
654
``` cpp
655
655
void f (){
656
656
if(!ptr){ // 1
657
- std::lock_guard<std::mutex>lk{ m };
657
+ std::lock_guard<std::mutex> lk{ m };
658
658
if(!ptr){ // 2
659
659
ptr.reset(new some); // 3
660
660
}
@@ -755,7 +755,7 @@ private:
755
755
756
756
public:
757
757
void set(const std::string& key, const std::string& value) {
758
- std::lock_guard< std::shared_mutex > lock( mutex_ ) ;
758
+ std::lock_guard< std::shared_mutex > lock{ mutex_ } ;
759
759
data_ [ key] = value;
760
760
}
761
761
@@ -767,7 +767,7 @@ public:
767
767
};
768
768
```
769
769
770
- > [ 完整代码] ( https://github.com/Mq-b/ModernCpp-ConcurrentProgramming-Tutorial/blob/main/code/03共享数据/保护不常更新的数据结构.cpp ) 。[ 测试] ( https://godbolt.org/z/KG84rb8qd ) 链接。标准输出可能交错,但无数据竞争。
770
+ > [ 完整代码] ( https://github.com/Mq-b/ModernCpp-ConcurrentProgramming-Tutorial/blob/main/code/03共享数据/保护不常更新的数据结构.cpp ) 。[ 测试] ( https://godbolt.org/z/ezh1Pdo5E ) 链接。标准输出可能交错,但无数据竞争。
771
771
772
772
` std::shared_timed_mutex ` 具有 ` std::shared_mutex ` 的所有功能,并且额外支持超时功能。所以以上代码可以随意更换这两个互斥量。
773
773
@@ -815,15 +815,15 @@ int main() {
815
815
816
816
```cpp
817
817
void recursive_function(int count) {
818
- std::lock_guard<std::recursive_mutex>lc{ mtx };
818
+ std::lock_guard<std::recursive_mutex> lc{ mtx };
819
819
std::cout << "Locked by thread: " << std::this_thread::get_id() << ", count: " << count << std::endl;
820
820
if (count > 0) {
821
821
recursive_function(count - 1);
822
822
}
823
823
}
824
824
```
825
825
826
- > [ 运行] ( https://godbolt.org/z/rqG613W94 ) 测试。
826
+ > [ 运行] ( https://godbolt.org/z/d63zrG1cK ) 测试。
827
827
828
828
## ` new ` 、` delete ` 是线程安全的吗?
829
829
0 commit comments