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;  }