是否有任何惯用的显式使用mutex::lock()或unlock()

Is there any idiomatic explicit use of mutex::lock() or unlock()?

本文关键字:unlock lock mutex 任何惯 是否      更新时间:2023-10-16

使用mutex锁定代码关键区域的推荐方法是通过RAII,即

mutex_type mutex;
{ // start of critical region
  std::lock_guard<mutex_type> lock(mutex);   // first statement in critical region
  // ... do critical stuff, may throw an exception
} // end of critical region

这样,当在关键区域内引发异常时,互斥锁仍将被解锁(由 std::lock_guard 的析构函数解锁)。但是,这样用户代码永远不会显式调用成员mutex::lock()mutex::unlock()

问:mutex::lock()的主要惯用语(如果有的话)是什么?

我在问,否则让mutex::lock()一个推广不良代码的公共成员(避免std::lock_guard)是没有意义的。

编辑由于std::lock_guard<>std::mutex都在同一标头中定义,因此std::mutex可以轻松地与std::lock_guard<std::mutex交朋友,并保护其lock()unlock()方法:

class mutex      // use only with lock_guard<mutex>
{
  friend class lock_guard<mutex>;         // support acquire-release semantic via RAII
  friend class scoped_lock_guard<mutex>;  // for supporting more complicated semantic,
                                          //     possibly remembering the mutex state.
  // ...
protected:
  void lock();
  bool try_lock();
  void unlock();       
};
class raw_mutex  // use if you absolutely must explicitly lock, try_lock, or unlock
: public mutex
{
public:
  using mutex::lock;
  using mutex::try_lock;
  using mutex::unlock;
};

回答我的问题的一个论点很简单,使用mutex::lock()的唯一异常安全方法是通过 RAII。因此,唯一合理的显式使用必须只涉及调用lock(或try_lock)和unlock之间的noexcept方法。但是,由于noexcept只是暗示性的,并且没有任何承诺,因此这种使用是不安全的。Q 对吗?

lock_guard并不是

唯一需要在mutex上调用lock/unlock的东西。 unique_locklocktry_lockcondition_variable_any都必须处理互斥体。 这只是标准类型。 在这种情况下,友谊引入了一种紧密的耦合,成为一种障碍。

当存在可以将资源的生存期绑定到的静态作用域时,可以使用 RAII,即资源应在进入作用域时初始化,并在退出作用域时释放。

但是,在某些情况下,没有这种静态范围。为了说明这一点,请将变量视为资源。当存在可以绑定它们的静态作用域时,我们使用自动变量,但有时我们需要动态变量来控制何时创建和销毁它们。

当我们使用 mutex es 进行互斥时,也会发生同样的情况。请考虑以下任务:

您的程序控制两个资源,它必须读取和执行一系列命令。可能的命令包括:

  • 锁定资源 #1 : 1>
  • 锁定资源 #2 : 2>
  • 解锁资源#1:1<
  • 解锁资源#2:2<
  • x写入资源 #1 : 1x
  • x写入资源 #2 : 2x

您的程序可以期待类似1> 1a 2> 2b 1c 1< 2d 2< ,如果程序服从命令,则无法对 RAII 使用静态作用域(作用域必须部分重叠)。所以我认为这说明了需要显式锁定/解锁的一种情况。


至少还有一种可能的情况,因为

互斥并不是唯一可以使用mutex的情况:它也可以用于同步。

考虑两个并行进程,PQ,每个进程在其代码中都有一个指定的点。我们要求Q在到达自己的点之前不能通过P点。可以通过使用初始化为locked状态的mutex并将lock()操作放在指定的Q点之前,将unlock()放在指定的P点之后来满足。可以轻松验证此设置是否解决了问题。

在这里,lock()放在一个进程中,unlock()放在另一个进程中,显然没有静态范围可以包围它们,因此我们需要两者都可以独立访问。