若是你将某个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()
。