使用多个锁导致的死锁问题
code 7:
#include <iostream>
#include <thread>
#include <mutex>
#include <string>
#include <fstream>
//std::mutex mu;
class LogFile{
public:
LogFile(){
//f.open("log.txt");
}
void log1(std::string s, int i){
std::lock_guard<std::mutex> lg1(m_mutex1);
std::lock_guard<std::mutex> lg2(m_mutex2);
std::cout<< " count i:" << i << std::endl;
}
void log2(std::string s, int i){
std::lock_guard<std::mutex> lg2(m_mutex2);
std::lock_guard<std::mutex> lg1(m_mutex1);
std::cout << " count i:" << i << std::endl;
}
protected:
private:
std::mutex m_mutex1;
std::mutex m_mutex2;
};
void test_funtion(LogFile& lf){
for (int i = 0; i < 100; ++i)
{
lf.log1("test_funtion ", i);
}
}
int main()
{
LogFile logFile;
std::thread t1(test_funtion,std::ref(logFile));
for (int i = 0; i < 100; ++i)
{
logFile.log2("main ", i);
}
t1.join();
return 0;
}
代码中log1和log2没有实际功能,只是为了测试,当需要多个锁时,若上锁的顺序不同,可能会发生死锁现象。t1线程执行到log1,锁住m_mutex1,马上要执行
std::lock_guard<std::mutex> lg2(m_mutex2);
主线程执行到log2,锁住m_mutex2,马上要执行
std::lock_guard<std::mutex> lg1(m_mutex,1);
这样t1会等待m_mutex2释放,而主线程会等待m_mutex1释放,这样就死锁了。所以要保证mutex上锁的顺序要一致,使用这种办法可以不用操心上锁顺序
void log1(std::string s, int i){
std::lock(m_mutex1, m_mutex2);
std::lock_guard<std::mutex> lg1(m_mutex1,std::adopt_lock);
std::lock_guard<std::mutex> lg2(m_mutex2, std::adopt_lock);
std::cout<< " count i:" << i << std::endl;
}
void log2(std::string s, int i){
std::lock(m_mutex1, m_mutex2);
std::lock_guard<std::mutex> lg2(m_mutex2, std::adopt_lock);
std::lock_guard<std::mutex> lg1(m_mutex1, std::adopt_lock);
std::cout << " count i:" << i << std::endl;
}
code8:
#include <iostream>
#include <thread>
#include <mutex>
#include <string>
#include <fstream>
class LogFile{
public:
LogFile(){
//f.open("log.txt");
}
void log1(std::string s, int i){
std::lock_guard<std::mutex> lg1(m_mutex1);
std::cout<< " count i:" << i << std::endl;
}
protected:
private:
std::mutex m_mutex1;
//std::fstream f;
};
void test_funtion(LogFile& lf){
for (int i = 0; i < 100; ++i)
{
lf.log1("test_funtion ", i);
}
}
int main()
{
LogFile logFile;
std::thread t1(test_funtion,std::ref(logFile));
for (int i = 0; i < 100; ++i)
{
logFile.log1("main ", i);
}
t1.join();
return 0;
}
可以使用unique_lock代替lock_guard,unique_lock使用更为灵活。
void log1(std::string s, int i){
std::unique_lock<std::mutex> lg1(m_mutex1);
std::cout<< " count i:" << i << std::endl;
lg1.unlock();//可灵活上锁解锁
lg1.lock();
lg1.unlock();
}
void log1(std::string s, int i){
std::unique_lock<std::mutex> lg1(m_mutex1,std::defer_lock);//未上锁
lg1.lock();
std::cout<< " count i:" << i << std::endl;
lg1.unlock();
lg1.lock();
lg1.unlock();
}
code9:
#include <iostream>
#include <thread>
#include <mutex>
#include <string>
#include <fstream>
std::mutex mu;
class LogFile{
public:
LogFile(){
f.open("log.txt");
}
void log(std::string s, int i){
std::lock_guard<std::mutex> lg(m_mutex);
f << s << " count i:" << i << std::endl;
}
protected:
private:
std::mutex m_mutex;//保护f
std::ofstream f;
};
void test_funtion(LogFile& lf){
for (int i = 0; i < 100; ++i)
{
lf.log("test_funtion ", i);
}
}
int main()
{
LogFile logFile;
std::thread t1(test_funtion, std::ref(logFile));
for (int i = 0; i < 100; ++i)
{
logFile.log("main ", i);
}
t1.join();
return 0;
}
LogFile在构造函数打开txt文件,这个没有必要,什么时候用什么时候打开就好。可以改成:
class LogFile{
public:
LogFile(){
}
void log(std::string s, int i){
if (!f.is_open())
{
std::lock_guard<std::mutex> lg(m_mutex_open);
f.open("log.txt");
}
std::lock_guard<std::mutex> lg(m_mutex);
f << s << " count i:" << i << std::endl;
}
protected:
private:
std::mutex m_mutex;//打印使用
std::mutex m_mutex_open;//打开文件使用
std::ofstream f;
};
这样也不是线程安全的,如果多个线程都执行至
std::lock_guard<std::mutex> lg(m_mutex_open);
一个线程释放m_mutex_open后,另一个线程会再次打开f,会导致f被打开两次。所以is_open应该和open同步,这样
void log(std::string s, int i){
{
if (!f.is_open())
{
std::lock_guard<std::mutex> lg(m_mutex_open);
f.open("log.txt");
}
}
std::lock_guard<std::mutex> lg(m_mutex);
f << s << " count i:" << i << std::endl;
}
频繁上锁解锁会影响性能,c++11提供了更好的办法
Lazy Initialization
class LogFile{
public:
LogFile(){
}
void log(std::string s, int i){
std::call_once(m_flag_open, [&](){f.open("log.txt"); });
std::lock_guard<std::mutex> lg(m_mutex);
f << s << " count i:" << i << std::endl;
}
protected:
private:
std::mutex m_mutex;//打印使用
std::once_flag m_flag_open;
//std::mutex m_mutex_open;//打开文件使用
std::ofstream f;
};