当前位置: 首页 > news >正文

c++介绍锁四

对于多线程来说导致死锁的原因较多,最简单的一个原因是锁定一个互斥量后,没有释放这个互斥量,导致其它的线程获取这个互斥量时,会永远进入阻塞状态,往往发生在程序分支较多或者抛出异常时忘记主动释放,解决方法:lock_guard或者unique_lock这样的自动对象,对互斥量进行管理,这样离开作用域后,在析构函数中自动释放互斥量。

另一个原因,同时对多个互斥量进行锁定和解锁时,由于获得和释放互斥量顺序不同容易造成死锁。

例如

#include<array>
#include<thread>
#include<iostream>
#include<mutex>
using namespace std;

class DemoClass
{
public:
	void func1()
	{
		{
			lock_guard<mutex> lock1(mtx1);
			lock_guard<mutex>lock2(count_mtx);
			count++;
			cout << "[func1]count=" << count << "\n";
		}
		this_thread::sleep_for(1ms);
	}
	void func2()
	{
		{
			lock_guard<mutex>lock1(count_mtx);
			lock_guard<mutex>lock2(mtx1);
			count--;
			cout << "[fuc2]Count=" << count << "\n";
		}
		this_thread::sleep_for(1ms);
	}
	void calc(int n)
	{
		for (int i = 0; i < n; i++)
		{
			if (n % 2)
			{
				func2();
			}
			else
			{
				func1();
			}
		}	
	}
private:
	mutex mtx1;
	mutex count_mtx;
	int count = 0;
};
void main()
{
	DemoClass demo;
	const int n1 = 10000;
	const int n2 = 10001;
	thread th1(&DemoClass::calc,&demo,n1);
	thread th2(&DemoClass::calc,&demo, n2);

	th1.join();
	th2.join();
}

运行结果 

程序运行一段时间后发生死锁。

线程1获取mtx1等待获取count_mtx,而线程2获取count_mtx等待获取mtx这样程序永远进入阻塞状态。

改进方法1代码如下

#include<array>
#include<thread>
#include<iostream>
#include<mutex>
using namespace std;

class DemoClass
{
public:
	void func1()
	{
		while (true){
			unique_lock<timed_mutex> lock1(mtx1,defer_lock);
			unique_lock<timed_mutex>lock2(count_mtx,defer_lock);
			if (!lock1.try_lock_for(100ms))
				continue;
			if (!lock2.try_lock_for(100ms))
				continue;
			count++;
			cout << "[func1]count=" << count << "\n";
		}
		this_thread::sleep_for(1ms);
	}
	void func2()
	{
		while(true){
			unique_lock<timed_mutex>lock1(count_mtx,defer_lock);
			unique_lock<timed_mutex>lock2(mtx1,defer_lock);
			if (!lock1.try_lock_for(100ms))
				continue;
			if (!lock2.try_lock_for(100ms))
				continue;
			count--;
			cout << "[fuc2]Count=" << count << "\n";
		}
		this_thread::sleep_for(1ms);
	}
	void calc(int n)
	{
		for (int i = 0; i < n; i++)
		{
			if (n % 2)
			{
				func2();
			}
			else
			{
				func1();
			}
		}	
	}
private:
	timed_mutex mtx1;
	timed_mutex count_mtx;
	int count = 0;
};
void main()
{
	DemoClass demo;
	const int n1 = 10000;
	const int n2 = 10001;
	thread th1(&DemoClass::calc,&demo,n1);
	thread th2(&DemoClass::calc,&demo, n2);

	th1.join();
	th2.join();
}

使用计时锁如果在100ms内为获取到想获取得锁,释放当前拥有得锁,进入下一次循环,这样程序进入到活锁得状态,两个线程不断得释放自己当前拥有得锁,而又得不到想获取得锁。

解决活锁得方法:保证顺序获得互斥量

#include<array>
#include<thread>
#include<iostream>
#include<mutex>
using namespace std;

class DemoClass
{
public:
	void func1()
	{
			lock(mtx1,count_mtx);
			lock_guard<mutex> lock1(mtx1,adopt_lock);
			lock_guard<mutex> lock2(count_mtx, adopt_lock);
			count++;
			cout << "[func1]count=" << count << "\n";
		   this_thread::sleep_for(1ms);
	}
	void func2()
	{
			lock(mtx1, count_mtx);
			lock_guard<mutex> lock1(mtx1, adopt_lock);
			lock_guard<mutex> lock2(count_mtx, adopt_lock);
			count--;
			cout << "[fuc2]Count=" << count << "\n";
		   this_thread::sleep_for(1ms);
	}
	void calc(int n)
	{
		for (int i = 0; i < n; i++)
		{
			if (n % 2)
			{
				func2();
			}
			else
			{
				func1();
			}
		}	
	}
private:
	mutex mtx1;
	mutex count_mtx;
	int count = 0;
};
void main()
{
	DemoClass demo;
	const int n1 = 10000;
	const int n2 = 10001;
	thread th1(&DemoClass::calc,&demo,n1);
	thread th2(&DemoClass::calc,&demo, n2);

	th1.join();
	th2.join();
}

算法大致原理

线程1拥有互斥量A,线程2拥有互斥量b,当前程1尝试获取互斥量b时,获取失败,线程1释放互斥量a,此时线程2获取互斥量a成功,线程2再获取互斥量c,拥有所有资源后执行线程2.线程2执行完毕后释放互斥量a,b,c。线程1获取互斥量a,b,c.执行线程1。

任何一个下线程在等待资源得时候,不能够锁定任何资源。如果已经获得资源,那么需要释放资源,再进入阻塞状态。即防止死锁条件得破环请求保持条件。

此外还可以使用scope_lock类更加方便。

相关文章:

  • vim 编写/etc/docker/daemon.json文件时,E212: 无法打开并写入文件
  • 红队思想:Live off the Land - 靠山吃山,靠水吃水
  • 算法 之 树形dp 树的中心、重心
  • 深入理解序列并行化:sp_size 与批量大小参数详解
  • git安装(windows)+vscode配置
  • 深入理解C语言链表:数据结构的基石
  • 注意力机制-学习
  • QML界面卡死分析--01 --Canvas与QRasterPaintEngine
  • Python:函数(一)
  • 【DDD的本质与核心思想】
  • 脚本学习(1)验证目录自动化生成脚本
  • 在rocklinux里面批量部署安装rocklinx9
  • mysql的锁--一篇读懂所有锁机制
  • C语言:确定进制
  • 多宠识别:基于计算机视觉的智能宠物管理系统架构解析
  • k8s启动时calico-kube-controllers与coredns组件一直是pending状态
  • 《 C++ 点滴漫谈: 三十 》函数参数
  • OpenCV实现视频背景提取
  • Go 语言封装 HTTP 请求的 Curl 工具包
  • 数据分析与AI丨AI Fabric:数据和人工智能架构的未来
  • 北美票房|华纳又赢了,《死神来了6》开画远超预期
  • 国家统计局:4月社会消费品零售总额同比增长5.1%
  • CBA官方对孙铭徽罚款3万、广厦投资人楼明停赛2场罚款5万
  • 媒体评教师拎起学生威胁要扔下三楼:师风师德不能“悬空”
  • 种植耐旱作物、启动备用水源,甘肃各地多举措应对旱情
  • 上海交大曾小勤:科技传播不应停留于知识搬运,要做科学思维的播种机