1 自动锁
创建一个类,将锁封装起来,类的构造函数自动将锁锁住,类的析构函数自动将锁释放。
template <typename T>
class MyLock{
private:
T& m_mutex;
public:
MyLock(T& v_mutex):m_mutex(v_mutex){
m_mutex.lock();
}
~MyLock(){
m_mutex.unlock();
}
};
在一个大括号范围内,即局部范围内,用MyLock生产一个栈对象,当局部结束时,自动调用析构函数,不需要手动的lock()和unlock()。
void test1(){
std::mutex mt;
MyLock<std::mutex> lock(mt);
//std::lock_guard<std::mutex> lock(mt);
//***
}
上面效果等同于std::mutex mt;std::lock_guard<std::mutex> lock1(mt);
2 模拟银行转账
class Bank{
public:
int m_money;
Bank(int m):m_money(m){}
std::mutex m_mutex;
};
第一个转账版本
//模拟用户转账
void transf1(Bank& a,Bank& b,int money){
if(a.m_money < money) return; //账户余额不足,后面就不进行判断了
std::lock_guard<std::mutex> lockA(a.m_mutex);
std::lock_guard<std::mutex> lockB(b.m_mutex);
a.m_money -= money;
b.m_money += money;
//这种情况会出现死锁
//例如:
//transf1(bob,bob,10); //自己给自己转账,就会出现死锁
}
两个账户,a向b转账。此时,a账户和b账户同时被锁住。但是如果出现自己给自己转账这种情况,就会无法避免,出现死锁情况。
第二个转账版本
void tranf2(Bank& a,Bank& b,int money){
if(&a == &b) return; //如果自己给自己转账,直接return
std::lock_guard<std::mutex> lockA(a.m_mutex);
std::lock_guard<std::mutex> lockB(b.m_mutex);
//... 转账操作
//在多线程会出现死锁
//std::thread t1(tranf2,std::ref(alice),std::ref(bob),10);
//std::thread t2(tranf2,std::ref(bob),std::ref(alice),10);
//会出现死等情况,线程1锁alice,线程2锁bob,线程1锁bob(进行不下去,因为bob已经被锁住,出现死等)
}
先进行判断,如果自己给自己转账,自己return掉。但是如果出现多线程转账,即张三给李四转账的同时,李四也给张三转账,此时就可能出现死锁。第一个线程张三给李四转账,张三被锁住,正准备锁李四,第二个线程李四给张三转账,李四被锁住,正准备锁张三,就会出现相互死等的情况。
第三个转账版本
void tranf3(Bank& a,Bank& b,int money){
//把a和b按照一定的地址排序,然后按照顺序加锁,就不会出现死等情况
std::lock(a.m_mutex,b.m_mutex,/*...*/); //按std某种规则全部锁上
std::lock_guard<std::mutex> lockA(a.m_mutex,std::adopt_lock);
std::lock_guard<std::mutex> lockB(b.m_mutex,std::adopt_lock);
//std::adopt_lock lock_guard构造时候,传递的对象可以已经被锁住
//转账操作
}
std::lock传参多个锁,然后按照自己的某种规则顺序锁住,避免了死锁的情况。
3 全部代码
#include <iostream>
#include <mutex>
#include <thread>
//加锁后自动解锁,将锁变成一个类似局部栈对象
template <typename T>
class MyLock{
private:
T& m_mutex;
public:
MyLock(T& v_mutex):m_mutex(v_mutex){
m_mutex.lock();
}
~MyLock(){
m_mutex.unlock();
}
};
void test1(){
std::mutex mt;
MyLock<std::mutex> lock(mt);
//std::lock_guard<std::mutex> lock(mt);
//***
}
class Bank{
public:
int m_money;
Bank(int m):m_money(m){}
std::mutex m_mutex;
};
//模拟用户转账
void transf1(Bank& a,Bank& b,int money){
if(a.m_money < money) return; //账户余额不足,后面就不进行判断了
std::lock_guard<std::mutex> lockA(a.m_mutex);
std::lock_guard<std::mutex> lockB(b.m_mutex);
a.m_money -= money;
b.m_money += money;
//这种情况会出现死锁
//例如:
//transf1(bob,bob,10); //自己给自己转账,就会出现死锁
}
void tranf2(Bank& a,Bank& b,int money){
if(&a == &b) return; //如果自己给自己转账,直接return
std::lock_guard<std::mutex> lockA(a.m_mutex);
std::lock_guard<std::mutex> lockB(b.m_mutex);
//... 转账操作
//在多线程会出现死锁
//std::thread t1(tranf2,std::ref(alice),std::ref(bob),10);
//std::thread t2(tranf2,std::ref(bob),std::ref(alice),10);
//会出现死等情况,线程1锁alice,线程2锁bob,线程1锁bob(进行不下去,因为bob已经被锁住,出现死等)
}
void tranf3(Bank& a,Bank& b,int money){
//把a和b按照一定的地址排序,然后按照顺序加锁,就不会出现死等情况
std::lock(a.m_mutex,b.m_mutex,/*...*/); //按std某种规则全部锁上
std::lock_guard<std::mutex> lockA(a.m_mutex,std::adopt_lock);
std::lock_guard<std::mutex> lockB(b.m_mutex,std::adopt_lock);
//std::adopt_lock lock_guard构造时候,传递的对象可以已经被锁住
//转账操作
}
int main()
{
test1();
return 0;
}