若是你將某個mutex
上鎖了,卻一直不釋放,另外一個線程訪問該鎖保護的資源的時候,就會發生死鎖,這種狀況下使用lock_guard
能夠保證析構的時候可以釋放鎖,然而,當一個操做須要使用兩個互斥元的時候,僅僅使用lock_guard
並不能保證不會發生死鎖,以下面的例子:ios
#include <iostream> #include <thread> #include <string> #include <mutex> #include <fstream> using namespace std; class LogFile { std::mutex _mu; std::mutex _mu2; ofstream f; public: LogFile() { f.open("log.txt"); } ~LogFile() { f.close(); } void shared_print(string msg, int id) { std::lock_guard<std::mutex> guard(_mu); std::lock_guard<std::mutex> guard2(_mu2); f << msg << id << endl; cout << msg << id << endl; } void shared_print2(string msg, int id) { std::lock_guard<std::mutex> guard(_mu2); std::lock_guard<std::mutex> guard2(_mu); f << msg << id << endl; cout << msg << id << endl; } }; void function_1(LogFile& log) { for(int i=0; i>-100; i--) log.shared_print2(string("From t1: "), i); } int main() { LogFile log; std::thread t1(function_1, std::ref(log)); for(int i=0; i<100; i++) log.shared_print(string("From main: "), i); t1.join(); return 0; }
運行以後,你會發現程序會卡住,這就是發生死鎖了。程序運行可能會發生相似下面的狀況:c++
Thread A Thread B _mu.lock() _mu2.lock() //死鎖 //死鎖 _mu2.lock() _mu.lock()
解決辦法有不少:編程
能夠比較mutex
的地址,每次都先鎖地址小的,如:併發
if(&_mu < &_mu2){ _mu.lock(); _mu2.unlock(); } else { _mu2.lock(); _mu.lock(); }
這兩種辦法其實都是嚴格規定上鎖順序,只不過實現方式不一樣。函數
c++
標準庫中提供了std::lock()
函數,可以保證將多個互斥鎖同時上鎖,this
std::lock(_mu, _mu2);
同時,lock_guard
也須要作修改,由於互斥鎖已經被上鎖了,那麼lock_guard
構造的時候不該該上鎖,只是須要在析構的時候釋放鎖就好了,使用std::adopt_lock
表示無需上鎖:spa
std::lock_guard<std::mutex> guard(_mu2, std::adopt_lock); std::lock_guard<std::mutex> guard2(_mu, std::adopt_lock);
完整代碼以下:線程
#include <iostream> #include <thread> #include <string> #include <mutex> #include <fstream> using namespace std; class LogFile { std::mutex _mu; std::mutex _mu2; ofstream f; public: LogFile() { f.open("log.txt"); } ~LogFile() { f.close(); } void shared_print(string msg, int id) { std::lock(_mu, _mu2); std::lock_guard<std::mutex> guard(_mu, std::adopt_lock); std::lock_guard<std::mutex> guard2(_mu2, std::adopt_lock); f << msg << id << endl; cout << msg << id << endl; } void shared_print2(string msg, int id) { std::lock(_mu, _mu2); std::lock_guard<std::mutex> guard(_mu2, std::adopt_lock); std::lock_guard<std::mutex> guard2(_mu, std::adopt_lock); f << msg << id << endl; cout << msg << id << endl; } }; void function_1(LogFile& log) { for(int i=0; i>-100; i--) log.shared_print2(string("From t1: "), i); } int main() { LogFile log; std::thread t1(function_1, std::ref(log)); for(int i=0; i<100; i++) log.shared_print(string("From main: "), i); t1.join(); return 0; }
總結一下,對於避免死鎖,有如下幾點建議:code
建議儘可能同時只對一個互斥鎖上鎖。資源
{ std::lock_guard<std::mutex> guard(_mu2); //do something f << msg << id << endl; } { std::lock_guard<std::mutex> guard2(_mu); cout << msg << id << endl; }
不要在互斥鎖保護的區域使用用戶自定義的代碼,由於用戶的代碼可能操做了其餘的互斥鎖。
{ std::lock_guard<std::mutex> guard(_mu2); user_function(); // never do this!!! f << msg << id << endl; }
std::lock()
。