多线程中互斥量、死锁和解决死锁的方法

这篇博客探讨了多线程编程中的互斥量概念,以及如何处理可能导致程序停滞的死锁问题。作者分享了自己的学习笔记,并承诺后续将深入整理相关内容。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

下面的代码是一点学习笔记,先记录一下,有时间的时候再过来把它整理一下。

#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
#include<string>
#include<fstream>
#include<vector>
#include<map>
#include<list>
#include<typeinfo>
#include<random>
#include<mutex>
#include<thread>
using namespace  std;

#if 0
void main()
{
	
	system("pause");
}
#endif


#if 0
void main()
{
	string str = "asdfghjkl";
	for (int i = 0; i < str.size(); i++)
	{
		cout << &str[i] << endl;
	}
	float a;
	map<int, int> m;
	cout << "******" << typeid(a).name() << endl;
	char *chr = new char[100];
	strcpy(chr, typeid(m).name());
	cout << chr << endl;

	

	system("pause");
}
#endif

#if 1
/*
lock与unlock一定要成对使用,否则就会出错。
lock_guard的构造函数中写进了lock
在析构函数中写进了unlock
所以lock_guard一般用于单独的一个函数中,起码在lock_guard的生命周期中它都
上锁的,一般将要上锁的部分单独拿出来写成函数,或者要对其作用域进行控制用{}
死锁:
至少有两把锁才能构成死锁
线程1是先锁锁1后锁锁2,线程2与线程1相反,这样就可能存在两个线程都在等待对方释放锁,而进入死锁。
死锁的一般解决方案:
1,只要保证两个互斥量上锁的次序一样
2,一次把所有的锁都上了,可以用std::lock()函数模板
该模板一次锁住两个或多个互斥量,不会因为上锁的次序问题导致死锁,如果有一个锁没有锁成功则将已经锁了的
马上释放掉,然后进入等到。
std::lock(my_mutex1,my_mutex2);
等价于:
my_mutex1.lock();
my_mutex2.lock();
但是这种还是需要两个unlock

3,是否可以用lock_guard和lock结合使用,这样就可以一次上多把锁,而且不用担心忘记是否?
写法为:
std::lock(my_mutex1,my_mutex2);
std::lock_guard<std::mutex> sbguard1(my_mutex1,std::adopt_lock);//std::adopt_lock这个参数就表示前面已经有上锁,这步就不执行上锁操作
std::lock_guard<std::mutex> sbguard2(my_mutex2,std::adopt_lock);

4,unique_lock是一个类模板,可以完全取代lock_guard,而且比lock_guard更加灵活
unique_lock同样也支持第二个参数,而且参数类型更丰富
std::unique_lock<std::mutex> sbguard(my_mutex,std::adopt_lock)//第二个参数起标记作用
std::adopt_lock:表示互斥量已经被lock了(必须提前把互斥量lock了,不然报错)
std::try_to_lock:程序会尝试使用mutex的lock去上锁,但是如果没有锁成功,程序也会立即返回,并不会阻塞
                ,用try_to_lock 的前提是不能先去上锁
std::defer_lock   前提是不能提前lock,使用后并没有给mutex加锁,初始化了一个没有加锁的mutex
             这样就可以使用unique_lock的一些成员函数
			 std::unique_lock<std::mutex> sbguard(my_mutex,std::defer_lock);//创建一个没有加锁的my_mutex
																			//相当于把sbguard和my_mutex绑定在一起
             sbguard.lock();//手动上锁,且不需要解锁
			 sbguard.unlock();//可以手动解锁,临时处理一些非共享代码,回来时再上锁就可以了
			 sbguard.try_lock();//尝试加锁,如果拿到锁则返回TRUE否则返回FALSE
			 sbguard.release();//返回它锁管理的mutex对象指针,并释放所有权,也就是说sbguard和mutex不在有关系
			                   //如果mutex监管的对象已经加锁还没有解锁,还要手动解锁
							   std::unique_lock<std::mutex>sbguard(my_mutex);
							   std::mutex *ptx=sbguard.release();
							   ..执行完共享代码后
							   ptx->unlock();
			这里解释一下为什么需要手动unlock:
			      我们把锁头锁住的代码多少称为锁的粒度,粒度一般用粗细来描述
				  粒度细效率高;粒度粗效率低
				  要学会使用合适粒度的代码来保护代码,既保证不能遗漏对共享数据的保护,也要照顾代码执行的效率
5,unique_lock所有权的传递,一个unique_lock和一个mutex绑定在一起
std::unique_lock<std::mutex>sbguard(my_mutex);//sbguard拥有my_mutex的所有权,这个所有权可以转移但是不能复制,也就是说只有一个对象能操作mutex
std::unique_lock<std::mutex>sbguard2(sbguard);//不能进行复制
std::unique_lock<std::mutex>sbguard2(std::move(sbguard));//sbguard就指向空了,sbguard2就指向mutex2
还有一种方法可以转移所有权,需要定义一个转移一个函数
std::unique_lock<std::mutex> rtn_unique_lock(my_mutex1)
{
	std::unique_lock<std::mutex> tmpguard(my_mutex);
	return tmpguard;
	//返回一个局部的unique_lock对象是可以的
	//这种返回局部对象的方式会导致系统生成一个临时的unique_lock对象,并调用unique_lock的移动构造函数
}

std::unique_lock<std::mutex> sbguard2=rtn_unique_lock(my_mutex1);//创建一个对象来承接这个临时对象即可



*/
class Message{
public:
	void in_message()
	{
		for (int i = 0; i < 100; i++)
		{
			/*my_mutex1.lock();
			my_mutex2.lock();
			my_message.push_front(i);
			my_mutex1.unlock();
			my_mutex2.unlock();*/

			/*std::lock(my_mutex1, my_mutex2);
			std::lock_guard<std::mutex> sbguard1(my_mutex1, std::adopt_lock);//std::adopt_lock这个参数就表示前面已经有上锁,这步就不执行上锁操作
			std::lock_guard<std::mutex> sbguard2(my_mutex2, std::adopt_lock);
			my_message.push_front(i);*/

			std::lock(my_mutex1, my_mutex2);
			std::unique_lock<std::mutex> sbguard1(my_mutex1, std::adopt_lock);//uniqu_lock完全可以替代lock_guard使用
			//std::unique_lock<std::mutex> sbguard2(my_mutex2, std::adopt_lock);
			std::unique_lock<std::mutex> sbguard2(my_mutex2, std::try_to_lock);
			my_message.push_front(i);

			cout << "向共享内存中存入数据:" << i << endl;
		}
	}
	void out(int &ret)
	{
		my_mutex1.lock();
		my_mutex2.lock();
		if (!my_message.empty())
		{
			
			ret = my_message.front();
			my_message.pop_front();
			cout << "从共享内存中取出数据:" << ret<< endl;
		}
		my_mutex1.unlock();
		my_mutex2.unlock();

	}
	void out_message()
	{
		//std::lock_guard<std::mutex> sbguard(my_mutex1);
		
		for (int i = 0; i < 100; i++)
		{
			int ret;
			out(ret);
		}
	}
private:
	list<int > my_message;
	mutex my_mutex1,my_mutex2;

};
/*
互斥量是一个是一个类对象,有成员函数lock和unlock
两者要成对使用
*/
int main()
{

	Message m;
	thread t_in(&Message::in_message,&m);
	
	thread t_out(&Message::out_message,&m);

	t_in.join();
	t_out.join();
	

	

	system("pause");
	return 0;
}
#endif


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值