C++ 多執行緒死鎖(引入lock函數)
- 2020 年 2 月 14 日
- 筆記
上一篇講了互斥鎖(傳送門)的用法,解決了多執行緒共享資源可能會造成的一些問題,那麼引入了鎖以後,其實也難免會造成一些問題,比如說忘記unlock,或者有兩個鎖a和b,一個鎖a在等待鎖b的解鎖,鎖b在等待鎖a的解鎖,這些情況都會造成程式的死鎖,比如下面這個例子:
#include <iostream> #include <thread> #include <mutex> void work1(std::mutex& mylock1, std::mutex& mylock2) { for (int i = 0; i < 100000; i++) { mylock1.lock(); mylock2.lock(); std::cout << "work1 : " << i << std::endl; mylock2.unlock(); mylock1.unlock(); } } void work2(std::mutex& mylock1, std::mutex& mylock2) { for (int i = 0; i < 100000; i++) { mylock2.lock(); mylock1.lock(); std::cout << "work2 : " << i << std::endl; mylock1.unlock(); mylock2.unlock(); } } int main() { std::mutex mylock1, mylock2; int ans = 0; std::thread t1(work1, std::ref(mylock1), std::ref(mylock2)); std::thread t2(work2, std::ref(mylock1), std::ref(mylock2)); t1.join(); t2.join(); return 0; }
由於交叉加鎖,使得兩個鎖都在等待對方解鎖而造成的死鎖,運行結果如下圖所示:

解決這個死鎖的問題只是把加鎖的順序改過來就可以了,然後也可以用std::lock函數來創建多個互斥鎖,用法也很簡單,首先創建兩個互斥鎖lock1和lock2,那麼std::lock(lock1,lock2)這句程式碼就相當於lock1.lock();lock2.lock();,最後不要忘了對兩個鎖的unlock,其實也可以搭配lock_guard()來使用,因為lock_guard內部就有析構函數來unlock,所以在lock_guard中引用std::adopt_lock參數(作用是告訴編譯器我已經lock過了,不需要再重複lock了)就可以實現省去後面的unlock語句了。程式碼如下:
#include <iostream> #include <thread> #include <mutex> void work1(std::mutex& mylock1, std::mutex& mylock2) { for (int i = 0; i < 100000; i++) { std::lock(mylock1, mylock2); std::lock_guard<std::mutex> lock1(mylock1, std::adopt_lock); std::lock_guard<std::mutex> lock2(mylock2, std::adopt_lock); std::cout << "work1 : " << i << std::endl; } } void work2(std::mutex& mylock1, std::mutex& mylock2) { for (int i = 0; i < 100000; i++) { std::lock(mylock1, mylock2); std::lock_guard<std::mutex> lock1(mylock1, std::adopt_lock); std::lock_guard<std::mutex> lock2(mylock2, std::adopt_lock); std::cout << "work2 : " << i << std::endl; } } int main() { std::mutex mylock1, mylock2; int ans = 0; std::thread t1(work1, std::ref(mylock1), std::ref(mylock2)); std::thread t2(work2, std::ref(mylock1), std::ref(mylock2)); t1.join(); t2.join(); return 0; }