是std :: lock_guard <std :: unique_lock <std :: mutex>&gt;有效还是推荐?

时间:2016-11-28 11:57:07

标签: c++ c++11

我需要在一个工作方法中对unique_lock :: owns_lock()断言,但我不想将unique_lock作为参数传递,而是将其用作静态变量。但这意味着我不能将unique_lock用于其RAII行为。 拥有如下代码是否公平:

namespace
{
    std::mutex gMtx;
    std::unique_lock<std::mutex> gLock(gMtx, std::defer_lock);
}

void foo()
{
    assert(gLock.owns_lock());
    // ...
}

void bar()
{
    std::lock_guard<std::unique_lock<std::mutex>> lock(gLock);
    //...
    foo();
    //...
}

思想?

2 个答案:

答案 0 :(得分:1)

标准尚不清楚这种结构的有效性。

std::lock_guard的模板参数必须符合BasicLockable requirements,即必须包含lock()unlock()函数。

std::unique_lock具有这些功能,这意味着代码可以编译。但是,它们不能像BasicLockable那样工作。 unlock()不应该抛出异常,但std::unique_lock::unlock()可以抛出异常。根据这一点,使用std::lock_guard<std::unique_lock<std::mutex>>应该是未定义的行为。

但是,正如Holt在评论中指出的那样,该标准还表示unique_lock符合BasicLockable要求。因此不清楚行为是否已定义。如果你可以保证unlock()没有抛出(在lock_guard被销毁之前,互斥锁没有被解锁),它可能会在实践中发挥作用。

答案 1 :(得分:0)

根据当前的C++ standard draft,这似乎是有效的,同一文字适用于C++11,只有一个段落编号更改。

The thing passed as the template argument to std::lock_guard must be a BasicLockable

BasicLockable指定了两件事:

  1. lock(),除了获取锁定抛出异常之外没有任何要求或限制。
  2. unlock()只能在持有锁定时调用,不能失败,不能抛出异常
  3. unique_lock::lock似乎满足第一个条款,但请参见下面的分隔线。

    unique_lock::unlock满足第二个条款的要求,因为unique_lock::unlock可能抛出的唯一情况是当前没有锁定,BasicLockable要求在调用unlock时保持锁定。

    这与我在isocpp.org std-discussion邮件列表中提出此阅读时收到的唯一回复相符。

    如果unique_lock已被占用,则似乎违反了BasicLockable的要求,因为当前execution agent持有锁,例外被扔了。由于确切的措辞是

      

    如果抛出异常,则不应为当前执行代理获取锁定。

    有人可能认为锁定并非获得,因为它已经被保留了。