我正在学习C ++,我发现范围锁的源代码非常简单。 。它是如何工作的,这是“资源获取是实例化”(RAII)的一个例子?
答案 0 :(得分:16)
以下是说明范围锁定的小代码:
void do_something()
{
//here in the constructor of scoped_lock, the mutex is locked,
//and a reference to it is kept in the object `lock` for future use
scoped_lock lock(shared_mutex_obj);
//here goes the critical section code
}//<---here : the object `lock` goes out of scope
//that means, the destructor of scoped_lock will run.
//in the destructor, the mutex is unlocked.
阅读评论。这解释了scoped_lock的工作原理。
以下是scoped_lock
通常实现的方式(最小代码):
class scoped_lock : noncopyable
{
mutex_impl &_mtx; //keep ref to the mutex passed to the constructor
public:
scoped_lock(mutex_impl & mtx ) : _mtx(mtx)
{
_mtx.lock(); //lock the mutex in the constructor
}
~scoped_lock()
{
_mtx.unlock(); //unlock the mutex in the constructor
}
};
答案 1 :(得分:10)
RAII(资源获取是初始化)的想法是创建一个对象并将其初始化连接成一个不可分割的动作。这通常意味着它们是在对象的构造函数中执行的。
Scoped锁通过在构造互锁时锁定互斥锁来工作,并在它们被破坏时解锁。 C ++规则保证当控制流离开作用域时(即使是异常),正在退出作用域本地的对象被正确销毁。这意味着使用范围锁而不是手动调用lock()
和unlock()
使得无法意外地解锁互斥锁,例如在lock()
和unlock()
之间的代码中间抛出异常时。
此原则适用于获取必须释放的资源的所有场景,而不仅仅是锁定互斥锁。为类似语法的其他操作提供此类“范围保护”类是一种好习惯。
例如,我最近研究过一个数据结构类,它通常在修改时发送信号,但是对于某些批量操作必须禁用这些信号。提供一个范围保护类,在构造时禁用它们并在销毁时重新启用它们,防止对禁用/启用功能的潜在不平衡调用。
答案 2 :(得分:3)
基本上它的工作原理如下:
template <class Lockable>
class lock{
public:
lock(Lockable & m) : mtx(m){
mtx.lock();
}
~lock(){
mtx.unlock();
}
private:
Lockable & mtx;
};
如果您使用它
int some_function_which_uses_mtx(){
lock<std::mutex> lock(mtx);
/* Work with a resource locked by mutex */
if( some_condition())
return 1;
if( some_other_condition())
return 1;
function_witch_might_throw();
return;
}
使用基于范围的生命周期创建新对象。每当剩下当前范围并且此锁被破坏时,它将自动调用mtx.unlock()
。请注意,在此特定示例中,互斥锁上的锁定是由lock
的构造函数获取的,即RAIII。
如果没有护卫队,你会怎么做?离开函数时,您需要调用mtx.unlock()
。这是a)麻烦且b)容易出错。此外,在没有范围保护的情况下返回后,您无法释放互斥锁。