【问题标题】:Is there any idiomatic explicit use of mutex::lock() or unlock()?是否有任何惯用的明确使用 mutex::lock() 或 unlock()?
【发布时间】:2014-04-04 21:11:38
【问题描述】:

使用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() 永远不会被用户代码显式调用。

Q如果有的话,mutex::lock() 的主要惯用显式用法是什么?

我在问,否则没有必要让 mutex::lock() 成为公共成员宣传不良代码(避免 std::lock_guard)。

编辑 由于std::lock_guard&lt;&gt;std::mutex 都定义在同一个标​​头中,std::mutex 可以轻松地与std::lock_guard&lt;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 只是暗示性的并且没有任何承诺,因此这样的使用是不安全的。 正确吗?

【问题讨论】:

  • @LightnessRacesinOrbit 你的代码看起来很吓人。 thread_start_lock.unlock() 总是由调用thread_start_lock.lock() 的同一线程调用,这一点并不明显,实际上看起来情况正好相反。
  • 是的,这就是重点。
  • @LightnessRacesinOrbit ???所以关键是你的代码唤起了UB?您至少应该添加一个断言它不是。
  • UB跟它有什么关系?

标签: c++ multithreading c++11 locking mutex


【解决方案1】:

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

【讨论】:

    【解决方案2】:

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

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

    当我们使用mutexes 进行互斥时,也会发生同样的情况。考虑这个任务:

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

    • 锁定资源#1:1&gt;
    • 锁定资源#2:2&gt;
    • 解锁资源#1:1&lt;
    • 解锁资源#2:2&lt;
    • x 写入资源#1:1x
    • x 写入资源#2:2x

    你的程序可以期待像1&gt;1a2&gt;2b1c1&lt;2d2&lt;这样的输入, 如果程序服从命令(范围必须部分重叠),这使得对 RAII 使用静态范围是不可能的。 所以我认为这说明了一种需要显式锁定/解锁的情况。


    至少还有另一种可能的情况,即互斥并不是唯一可以使用mutex的情况:它也可以用于同步。

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

    这里lock() 放在一个进程中,unlock() 放在另一个进程中,同样显然没有静态作用域可以包含它们,所以我们需要两者都可以独立访问。

    【讨论】:

      猜你喜欢
      • 2013-09-02
      • 2016-11-21
      • 1970-01-01
      • 2017-03-28
      • 1970-01-01
      • 2021-09-12
      • 1970-01-01
      • 2014-05-16
      • 1970-01-01
      相关资源
      最近更新 更多