【问题标题】:Does a mutex unlock when it comes out of scope?超出范围时互斥锁会解锁吗?
【发布时间】:2013-07-18 16:45:28
【问题描述】:

简单的问题 - 基本上,我是否必须解锁互斥锁,或者我可以简单地使用范围运算符并且互斥锁会自动解锁?

即:

{ 
    pthread_mutex_lock (&myMutex); 
    sharedResource++; 
} // my mutex is now unlocked?

或者我应该:

{ 
    pthread_mutex_lock (&myMutex); 
    sharedResource++;
    pthread_mutex_unlock (&myMutex);
}

【问题讨论】:

  • 您使用的是哪种互斥锁实现?除非您的互斥锁实现支持 RAII,否则您可能必须显式解锁互斥锁。
  • 感谢@Void。我正在使用 pthread.h。即pthread_mutex_unlock() 和pthread_mutex_lock()。什么是 RAII?
  • @AmitNayar:见en.wikipedia.org/wiki/Resource_Acquisition_Is_Initialization。它可能是 C++ 中最重要的习惯用法,因为没有它几乎不可能正确管理内存和其他动态资源。
  • 您应该编辑您的代码以显示您正在使用pthread_mutex_lock 等。这很重要。没有人知道mutex_lock 是什么。

标签: c++ multithreading pthreads mutex


【解决方案1】:

在您的示例中,互斥锁并没有超出范围;并且编译器无法知道特定函数需要在作用域末尾调用,因此第一个示例不解锁互斥锁。

如果您使用(容易出错的)函数来锁定和解锁互斥锁,那么您需要确保始终调用unlock() - 即使受保护的操作会引发异常。

最好的方法是使用RAII 类来管理锁,就像任何其他需要在使用后释放的资源一样:

class lock_guard {
public:
    explicit lock_guard(mutex & m) : m(m) {mutex_lock(m);}
    ~lock_guard() {mutex_unlock(m);}

    lock_guard(lock_guard const &) = delete;
    void operator=(lock_guard &) = delete;

private:
    mutex & m;
};

// Usage
{
    lock_guard lock(myMutex);
    shared_resource++;
} // mutex is unlocked here (even if an exception was thrown)

在现代 C++ 中,为此使用 std::lock_guard 或 std::unique_lock。

【讨论】:

    【解决方案2】:

    使用 RAII 作用域方法要好得多,因为它保证互斥体即使在遇到异常或提前返回时也将始终解锁。

    如果您可以访问 C++11,但您可能会考虑使用 std::atomic<int>,在这种情况下,您不需要锁定它来增加。

    【讨论】:

      【解决方案3】:

      在这种情况下,当代码超出范围时,不会解锁互斥锁。

      RAII 之后的互斥锁使用这样一个事实,即当非堆分配的对象超出范围时会自动调用析构函数。一旦锁定互斥锁的对象超出范围,它就会解锁互斥锁。对于您的代码,在大括号的范围内没有分配任何对象,因此一旦范围结束,互斥锁就不可能被解锁。

      例如,使用 Qt 库中的 QMutexLocker,您可以确保在作用域结束时解锁互斥锁:

      {
          QMutexLocker locker(myMutex);
          if(checkSomething())
          {
              return;
          }
          doSomething();
      }
      

      这段代码类似于:

      {
          mutex_lock(myMutex);
          if(checkSomething())
          {
              mutex_unlock(myMutex);
              return;
          }
          doSomething();
          mutex_unlock(myMutex);
      }
      

      尽管正如 Brian Neal 指出的那样,它并不能安全地处理 checkSomething() 和 doSomething() 抛出异常的情况。

      Qt 的 QMutexLocker 的替代方案是 STD 的 std::lock_guard。

      【讨论】:

      • 如果考虑异常,这两个代码 sn-ps 是不等价的。为了使它们等效,您必须在 checkSomething 和 doSomething 周围添加 try/catch 块。更喜欢使用 RAII 的第一个示例的另一个原因。 :)
      • 好点,@BrianNeal。我已经更新了答案。我强调不要使用异常,因为它们对于 Qt 基础架构并不是真正必要的,所以我很容易忘记其他人需要考虑它们。
      猜你喜欢
      • 2018-05-23
      • 2012-12-25
      • 2022-07-31
      • 1970-01-01
      • 2012-10-18
      • 2010-11-22
      • 1970-01-01
      • 2021-12-23
      相关资源
      最近更新 更多