C++多线程学习[五]:RAII锁

一、什么是RAII

使用局部对象来控制资源的技术,即它的生命周期由操作系统来管理,无需人工的介入。
为什么要采用RAII技术呢? 主要是在开发过程中资源的销毁容易忘记,容易造成死锁或内存泄露。

{}为一个区域 ,这里锁的是一块区域。不用在冗余的写mutex.lock()和mutex.unlock();

二、手动实现RAII管理mutex资源

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

class Tmutex
{
public:
	Tmutex(mutex& mtx) : mtx_(mtx)
	{
		cout << "Lock" << endl;
		//mtx_.lock();
	}

	~Tmutex()
	{
		cout << "UnLock" << endl;
		//mtx_.unlock();
	}
private:
	mutex &mtx_;
};

static mutex mtx;
void Test(int i)
{
	Tmutex lock(mtx);
	if (i == 1)
	{
		cout << " i = 1 " << endl;
	}
	else if (i == 2)
	{
		cout << " i = 2" << endl;
	}
	else 
	{
		cout << " i = " <<i<< endl;
	}

}

int main() 
{
	Test(1);
	Test(2);
	Test(3);
	return 0;
}

在这里插入图片描述

在这种方式中,lock 是一个对象,它被定义为互斥锁 mtx 的一个“保护者”(guard)。当创建 lock 对象时,在其构造函数中会自动执行 mtx.lock() 方法,从而获取互斥锁,确保当前线程拥有了对共享资源的独占访问权。而在 lock 对象生命周期结束时,其析构函数会自动调用 mtx.unlock() 方法,从而释放互斥锁,确保其他线程可以继续访问共享资源。

三、C++11 RAII控制锁lock_guard

在这里插入图片描述
可以看到 lock_guard源码中的构造会自动上锁,析构会自动解锁。

#include<iostream>
#include<thread>
#include<mutex>

using namespace std;

static mutex mtx;

void Test(int i)
{
	lock_guard<mutex> lock(mtx);
	for (;;)
	{
		cout << i << endl;
		this_thread::sleep_for(500ms);
	}
}
int main() 
{
	for (int i = 0; i < 3; i++)
	{
		thread th(Test, i + 1);
		th.detach();
	}
	getchar();
	return 0;
}

在这里插入图片描述
这里三个线程,但是i = 2 这个线程被锁住,

当然它还有另一个构造函数,adopt_lock_t用来判断是否已经上锁。
在这里插入图片描述
在这里,因为lock_guard锁的区域并没有检查到是否上锁,因此会报错。
(和下面unique_lock一样)
在这里插入图片描述

在这里插入图片描述
我们在他之前锁上
在这里插入图片描述

这里它检测到前面已经上锁,因此会通过。

四、unique_lock

在这里插入图片描述
源码中有四个构造函数。
主要的是后三个(括号内指的是unique_lock对象)

  • adopt_lock 文章前面涉及到(已经拥有锁,不加锁,出栈区后会释放)

  • defer_lock (延后拥有锁,不加锁,出栈区后不释放,加锁后,退出栈区释放)
    在这里插入图片描述
    在这里插入图片描述
    锁加在哪里都可以,这里3个线程都延时后i = 2 的先上锁。 因为不释放,所以不加锁的话这3个线程会一直运行,而不发生阻塞,直到其中一个被上锁。

  • try_to_lock(尝试获得互斥的所有权而不阻塞,获取失败退出栈区不会释放,通过owns_locc()函数判断 )
    在这里插入图片描述
    如果已经锁上,会尝试读取是否上锁,因为前面已经上锁,因此它不会再次上锁,所以进入else情况

当然它也可以自己上锁
在这里插入图片描述

  • 13
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
C++多线程编程中,是一种机制,用于控制对共享资源的访问。当多个线程同时读写同一个共享资源时,就会发生竞争条件(race condition)。为了避免这种情况,我们需要使用来确保每个线程都能按照预期方式访问共享资源。 在C++中,常用的包括互斥锁(mutex)、读写(read-write lock)和条件变量(condition variable)。 互斥锁是最基本的,用于保护对共享资源的互斥访问。当一个线程获得了互斥锁后,其他线程就无法访问共享资源,直到该线程释放互斥锁的使用方法如下: ```c++ #include <mutex> std::mutex m; void my_function() { std::lock_guard<std::mutex> lock(m); // 访问共享资源的代码 } ``` 这里使用了std::lock_guard类,它是一个RAII(资源获取即初始化)对象,用于自动获取和释放。在my_function函数中,当std::lock_guard对象被创建时,它会自动获取互斥锁;当my_function函数返回时,std::lock_guard对象会自动释放。 读写允许多个线程同时读取共享资源,但只允许一个线程写入共享资源。当一个线程获得了写时,其他任何线程都无法访问共享资源。当一个线程获得了读时,其他线程也可以获得读,但不能获得写。使用读写的方法如下: ```c++ #include <shared_mutex> std::shared_mutex m; void read_function() { std::shared_lock<std::shared_mutex> lock(m); // 读取共享资源的代码 } void write_function() { std::unique_lock<std::shared_mutex> lock(m); // 写入共享资源的代码 } ``` 这里使用了std::shared_lock类和std::unique_lock类,它们分别用于获取读和写。当std::shared_lock对象被创建时,它会自动获取读;当std::unique_lock对象被创建时,它会自动获取写。与互斥锁一样,这些也是RAII对象,会自动释放。 条件变量用于在线程之间传递信号,以便某个线程可以等待另一个线程发出的信号。条件变量通常与互斥锁一起使用。使用条件变量的方法如下: ```c++ #include <condition_variable> std::mutex m; std::condition_variable cv; void my_function() { std::unique_lock<std::mutex> lock(m); // 等待信号的代码 cv.wait(lock); // 收到信号后的代码 } void send_signal() { cv.notify_one(); } ``` 这里使用了std::condition_variable类,它用于等待信号和发送信号。在my_function函数中,当cv.wait(lock)被调用时,当前线程会被阻塞,直到收到信号;在send_signal函数中,cv.notify_one()用于发送一个信号,唤醒一个等待线程。与互斥锁和读写一样,std::unique_lock也是RAII对象,会自动释放

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Rain_ZZX

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值