作用域锁定是如何工作的



我正在学习c++,我看到作用域锁的源代码非常简单。它是如何工作的,这是"资源获取即实例化"(RAII)的一个例子吗?

下面是一小段代码来说明作用域锁:

 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
      }
};

RAII(资源获取即初始化)的思想是将创建对象和初始化对象连接在一起,形成一个不可分离的动作。这通常意味着它们在对象的构造函数中执行。

作用域锁的工作原理是:在互斥锁被构造时锁定它,在互斥锁被销毁时解锁它。c++规则保证当控制流离开一个作用域时(即使是通过异常),被退出的作用域的局部对象将被正确地销毁。这意味着使用作用域锁而不是手动调用lock()unlock(),不可能意外地不解锁互斥锁,例如,当在lock()unlock()之间的代码中间抛出异常时。

这个原则适用于所有获取必须释放的资源的场景,而不仅仅是锁定互斥锁。为其他具有类似语法的操作提供这样的"作用域保护"类是一个很好的实践。

例如,我最近研究了一个数据结构类,当它被修改时通常会发送信号,但是对于一些批量操作必须禁用这些信号。提供一个作用域保护类,在构造时禁用它们,在销毁时重新启用它们,可以防止对disable/enable函数的潜在不平衡调用。

基本上是这样的:

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)容易出错的。如果没有作用域保护,也不能在返回后释放互斥锁

最新更新