20200108自动锁、模拟转账

3 篇文章 0 订阅
3 篇文章 0 订阅

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;
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值