一、什么是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情况
当然它也可以自己上锁