与普通互斥锁相比,唯一锁如何工作?

How do unique locks work compared to normal mutex locks?

我遇到了 book 提供的代码示例。顺便说一句,这本书的评论很差。后悔买了

std::mutex m_mutex;
mutable std::unique_lock<std::mutex> m_finishedQueryLock{ m_mutex, std::defer_lock };

bool m_playerQuit{ false };
void SetPlayerQuit()
{
    m_finishedQueryLock.lock();
    m_playerQuit = true;
    m_finishedQueryLock.unlock();
}

我不满意本书对它的工作原理以及我为什么要使用它的解释。我已经对互斥锁的工作原理及其实现有所了解,但我很难理解上面代码的第二行。为什么它里面有一个可变关键字?

我对 C++ 编程完全陌生。所以基本水平的解释会对我有很大帮助。

unique_lock 是一个 RAII 系统。创建时,以 mutex 作为参数,它将锁定互斥锁,并在离开作用域时销毁它,从而解锁互斥锁。当你需要提前解锁时,你可以像你的例子一样调用unlock()函数。

像在您的示例中那样使用 unique_lock 并不会比直接使用互斥锁产生更多的价值。

这个例子看起来很愚蠢。

第二行声明了一个非静态数据成员,并且

  • 成员是 mutable(原因如下);
  • 该成员是 std::unique_lock<std::mutex> 类型的对象,它是 locking/unlocking 关联互斥对象的辅助类型;
  • 成员在创建 class 的实例时初始化,方法是调用其构造函数并将 m_mutex 和特殊标记 std::defer_lock 作为参数传递。

但这样做是愚蠢的,如果这本书有这样的例子,我不会感到惊讶。

unique_lock 的要点是锁定关联的互斥锁,然后在它超出范围时自动解锁。像这样创建一个 unique_lock 成员是愚蠢的,因为它不会在函数末尾超出范围,所以代码绝对没有优势:

mutable std::mutex m_mutex;

bool m_playerQuit{ false };

void SetPlayerQuit()
{
    m_mutex.lock();
    m_playerQuit = true;
    m_mutex.unlock();
}

但是这个手动解锁具有 unique_lock 旨在解决的所有问题,因此它应该使用范围锁(unique_locklock_guard)但仅限于功能范围,不作为成员:

mutable std::mutex m_mutex;

bool m_playerQuit{ false };

void SetPlayerQuit()
{
    std::lock_guard<std::mutex> lock(m_mutex);
    m_playerQuit = true;
}   // m_mutex is automatically unlocked by the ~lock_guard destructor

mutable 关键字是必需的,以便您可以在 const 成员函数中锁定互斥锁。锁定和解锁互斥量是一种修改互斥量的非常量操作,如果它不是可变的,则在 const 成员中是不允许的。

所有答案都很好地解释了为什么这个例子不好。当您想使用 std::unique_lock 而不是 std::lock_guard.

时,我会回答

当线程正在等待某事时,可以使用 std::condition_variable。条件变量使用延迟锁定,因此使用 unique_lock,它允许延迟锁定。我认为您不需要像示例中那样明确说明需要延迟锁定。

要掌握这些概念,首先需要了解什么是RAII。那就看看this reference. An example of the condition variables can be found here.