C++多线程 mutex unique_lock



普通的lock和unlock运用

互斥锁种类

互斥锁:mutex
带有超时机制的互斥锁:timed_mutex
递归互斥锁:recursive_mutex
带有超时机制的递归互:recursive_timed_mutex

mutex

加锁的理由如下图
【确保同一时间只有一个线程访问共享资源】
【访问共享资源前加锁,使用后解锁】

void fun(int ret) {
	flag.lock();
	cout << "我是" << ret << "号,我来上厕所了,我把门关上了" << endl;
	Sleep(1000);
	cout << "我是" << ret << "号,我上完了,我把门打开了,有请下一位" << endl;
	Sleep(500);
	flag.unlock();
}

在这里插入图片描述

recursive_mutex递归互斥锁[重要]

mutex无法解决已经加锁的情况下下一个函数又要用这个互斥量(又要加锁)的问题,如下图
在这里插入图片描述
所以引入递归互斥锁(recursive_mutex)解决这个问题
在这里插入图片描述

timed_mutex超时机制互斥锁

一个线程被堵塞过久可以先执行

demo 之lock、unlock

lock和unlock成对出现> if前用lock,则再if和else处各用一次unlock

#include <iostream>
using namespace std;
#include <thread>
#include <list>
#include <vector>
#include <mutex>//
//只读的数据不用加锁
//有独有写药用用mutex【不能同时读,不能同时写,a写bcd都不能读】
vector <int> ret(10, 1);//全局数据【共享数据】
//线程入口函数
void show(int val) {
	cout << "show--->" << val << endl;
	cout << "线程ID:" << this_thread::get_id() << endl << ret[0] << ret[1] << ret[2] << endl;
}
//
class stu {
public:
	//lock和unlock是成对出现
	void inque() {
		for (int i = 0; i < 10000; i++) {
			cout << "inque函数(),插入一个元素" << i << endl;
			signal.lock();
			myque.push_back(i);
			signal.unlock();
		}
	}
	bool outsuo(int& com) {
		signal.lock();
		if (!myque.empty()) {
			int com = myque.front();
			myque.pop_front();
			signal.unlock();
			return 1;
		}
		signal.unlock();
		return 0;
	}
	void outque() {
		int com = 0;
		for (int i = 0; i < 10000; i++) {
			bool res = outsuo(com);
			if (res) {
				cout<<"outque函数,去除元素"<<com<<endl;

			}
			else {
				cout << "outque函数()消息队列为空:" << i << endl;
			}
		}
	}

private:
	list <int> myque;
	mutex signal;//创建互斥量成员变量
};
void test01();//创建多个线程
void test02();
int main() {
	//test01();
	test02();

}
void test02() {
	stu s1;
	thread myin(&stu::inque, &s1);//用引用保证同一个对象
	thread myout(&stu::outque, &s1);
	myin.join();
	myout.join();
}
void test01() {
	list <thread> myth;
	for (int i = 0; i < 10; i++)
		//多个线程运行顺序是乱的
		myth.push_back(thread(show, i));//创建线程并已经开始执行[上下文切换]
	for (auto it = myth.begin(); it != myth.end(); it++) {
		//it->detach();
		it->join();
	}
}

lock_guard 替代lock和unlock

lock_guard 替代lock和unlock,之后lock、unlock不得再出现

格式

std::lock_guard <std::mutex> lock_guard_name(signal); 
void fun(int ret) {
	lock_guard <mutex> name(flag);
	cout << "我是" << ret << "号,我来上厕所了,我把门关上了" << endl;
	Sleep(1000);
	cout << "我是" << ret << "号,我上完了,我把门打开了,有请下一位" << endl;
	Sleep(500);
}

demo 之 lock_guard

void inque() {

		for (int i = 0; i < 10000; i++) {
			cout << "inque函数(),插入一个元素" << i << endl;
			{//用大括号可以提前结束lock_guard
				lock_guard <mutex>lock_guard_name(signal);
				//lock_guard在析构时才解锁
				myque.push_back(i);
			}
			//signal.unlock();
		}
	}
	bool outsuo(int& com) {
		//signal.lock();
		lock_guard <mutex>lock_guard_name(signal);
		if (!myque.empty()) {
			com = myque.front();
			myque.pop_front();
			//signal.unlock();
			return 1;
		}
		//signal.unlock();
		return 0;
	}
	void outque() {
		int com = 0;
		for (int i = 0; i < 10000; i++) {
			bool res = outsuo(com);
			if (res) {
				cout<<"outque函数,去除元素"<<com<<endl;

			}
			else {
				cout << "outque函数()消息队列为空:" << i << endl;
			}
		}
	}

死锁(至少两个互斥量)

死锁—>至少有两个互斥量
【两个互斥量上锁顺序一致不会死锁】
锁的代码少,则粒度细,运行效率高
🔒的代码多,则粒度粗,运行效率低
【只🔒与共享数据有关的数据】

案例(上锁顺序不一致,进入阻塞态)

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

解决方法

加锁顺序一致

(不展示)

运用lock同时锁两个互斥量

lock(signal,signal1);//同时上锁两个或多个,且不互锁
//注意解锁没有unlock两个参数,要分开写
signal.unlock();
signal1.ulock();
格式

lock(signal1,signal);//同时锁比较少用
//lock_guard用adopt_lock要一个一个写

lock+lock_guard可以不用unlock

//使用adopt_lock[可选择lock时要先lock,可以不再unlock]
lock(signal1,signal);
lock_guard <mutex> guard_name(signal,adopt_lock);
lock_guard <mutex>gard_name_1(signal1,adopt_lock);

unique_lock

unique_lock取代lock_gurad

[格式同lock_guard]
也可以
unique_lock < mutex> uni_name(signal);
【lock之后才可以adopt_lock】

在这里插入图片描述

用try _to_lock代替adopt_lock

此处不能先lock信号量
否则会锁两次

	unique_lock <mutex> guard_name(signal, try_to_lock);
		

defer_lock [不能先lock,初始化没加🔒的mutex]

defer_lock表示目前不加锁,之后可能加锁,在使用unique_lock的lock方法时会检查当前是否拥有锁,如果拥有就会报错

unique_lock <mutex> guard_name(signal, defer_lock);

unique_lock成员函数

[unique_lock的lock和unlock不必一一对应,但是不可在unique_lock之上lock]
【但可以一一对应,也可不一一对应】

lock

unlock

try_lock[尝试给互斥量加🔒]

relesae

release[指针][把unique_lock和mutex之间的关系断开]

释放后,👨‍💻有义务自己unlock

	bool outsuo(int& com) {
		unique_lock<mutex>uni_name(signal);
		mutex *ptr = uni_name.release();//释放后,👨‍💻有义务自己unlock

		chrono::milliseconds dura(200);
		this_thread::sleep_for(dura);
		if (!myque.empty()) {
			com = myque.front();
			myque.pop_front();
			ptr->unlock();//释放后,👨‍💻有义务自己unlock

			return 1;
		}
		ptr->unlock();//释放后,👨‍💻有义务自己unlock

		return 0;
	}
转移所有权也可以实现release
unique_lock <mutex> guard_name(signal, try_to_lock);
//转移所有权[move移动语义]
//guard_name1与signal绑定,guard_name已经解除关系
unique_lock<mutex>guard_name1(move(guard_name));
可通过函数返回值【存在问题】

在这里插入图片描述

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值