c++ 防范死锁
- 死锁:多线程并发程序中,线程之间相互等待不可抢占的资源,从而造成死锁。
死锁案例
-
两个线程互相等待互斥锁。从而导致死锁。
#include <cstdio> #include <thread> #include <mutex> std::mutex m1; std::mutex m2; void f(void) { std::lock_guard k(m1); printf("get lock 1\n"); std::lock_guard k1(m2); printf("get lock 2\n"); } void f1(void) { std::lock_guard k(m2); printf("get lock 2\n"); std::lock_guard k1(m1); printf("get lock 1\n"); } int main() { std::thread t(f); std::thread t1(f1); t.join(); t1.join(); printf("main exit\n"); return 0; }
解决方法
-
使用相同的顺序获取锁。不小心即会犯错。
-
使用
std::lock
同时获取多把锁。std::lock 可以同时锁住多个互斥,但不会自动解锁,需要配合 std::lock_guard 或者 std::unique_lock 使用,使其能够自动解锁。 -
使用
std::scoped_lock
同时获取多把锁。std::scoped_lock 与 std::lock_guard 完全等价,只不过前者是可变函数模板,可以接收多个互斥。#include <cstdio> #include <thread> #include <mutex> std::mutex m1; std::mutex m2; static int method = 3; // 需要 c++17 void f(void) { // 方法 1,按照相同的顺序获取锁 if (0 == method) { std::lock_guard k(m1); printf("thread 1 get lock 1\n"); std::lock_guard k1(m2); printf("thread 1 get lock 2\n"); } // 方法 2,std::lock 与 std::lock_guard 配合使用 if (1 == method) { // 锁住 m1 和 m2 std::lock(m1, m2); printf("thread 1 get lock 1 and 2\n"); // std::adopt_lock 指明互斥已被锁住 std::lock_guard k(m1, std::adopt_lock); std::lock_guard k1(m2, std::adopt_lock); } // 方法 3,std::lock 与 std::unique_lock 配合使用 if (2 == method) { // std::defer_lock 指明不需要对互斥进行加锁 std::unique_lock k(m1, std::defer_lock); std::unique_lock k1(m2, std::defer_lock); // 锁住 k 和 k1 std::lock(k, k1); printf("thread 1 get lock 1 and 2\n"); } // 方法 4,使用 std::scoped_lock if (3 == method) { std::scoped_lock k(m1, m2); printf("thread 1 get lock 1 and 2\n"); } } void f1(void) { if (0 == method) { std::lock_guard k(m1); printf("thread 2 get lock 1\n"); std::lock_guard k1(m2); printf("thread 2 get lock 2\n"); } if (1 == method) { std::lock(m1, m2); printf("thread 2 get lock 1 and 2\n"); std::lock_guard k(m1, std::adopt_lock); std::lock_guard k1(m2, std::adopt_lock); } if (2 == method) { std::unique_lock k(m1, std::defer_lock); std::unique_lock k1(m2, std::defer_lock); std::lock(k, k1); printf("thread 2 get lock 1 and 2\n"); } if (3 == method) { std::scoped_lock k(m1, m2); printf("thread 2 get lock 1 and 2\n"); } } int main() { std::thread t(f); std::thread t1(f1); t.join(); t1.join(); printf("main exit\n"); return 0; }
如何预防死锁
- 避免使用嵌套锁,若真的需要嵌套锁,应使用
std::scoped_lock
; - 依从固定顺序获取锁。