mutex
上锁了,却一直不释放,另一个线程访问该锁保护的资源的时候,就会发生死锁,这种情况下使用lock_guard
可以保证析构的时候能够释放锁,然而,当一个操作需要使用两个互斥元的时候,仅仅使用lock_guard
并不能保证不会发生死锁,如下面的例子:#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;}
运行之后,你会发现程序会卡住,这就是发生死锁了。程序运行可能会发生类似下面的情况:
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()
函数,能够保证将多个互斥锁同时上锁,std::lock(_mu, _mu2);
同时,lock_guard
也需要做修改,因为互斥锁已经被上锁了,那么lock_guard
构造的时候不应该上锁,只是需要在析构的时候释放锁就行了,使用std::adopt_lock
表示无需上锁:
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;}
总结一下,对于避免死锁,有以下几点建议:
std::lock()
。