为什么在执行条件变量通知之前我们需要一个空的 std::lock_guard?
Why do we need an empty std::lock_guard before doing condition variable notify?
我目前正在研究谷歌的灯丝工作系统。您可以在此处找到源代码。让我感到困惑的部分是这个requestExit()方法:
void JobSystem::requestExit() noexcept {
mExitRequested.store(true);
{ std::lock_guard<Mutex> lock(mLooperLock); }
mLooperCondition.notify_all();
{ std::lock_guard<Mutex> lock(mWaiterLock); }
mWaiterCondition.notify_all();
}
我很困惑为什么我们需要锁定和解锁,即使锁定和解锁之间没有操作。是否有任何情况需要这种空锁和解锁?
这有点黑客。首先,让我们看一下没有它的代码:
mExitRequested.store(true);
mLooperCondition.notify_all();
这里可能存在竞争条件。其他一些代码可能已经注意到mExitRequested
是假的,并在我们调用notify_all
后立即开始等待mLooperCondition
。
比赛将是:
- 其他线程检查
mExitRequested
,false
。 - 我们将
mExitRequested
设置为true
. - 我们称
mLooperCondition.notify_all
. - 其他线程等待
mLooperCondition
。 - 哎呀。等待已经发生的通知。
但是,为了等待条件变量,您必须持有关联的互斥锁。因此,只有当其他线程持有mLooperLock
互斥锁时,才会发生这种情况。实际上,步骤 4 实际上是:"其他线程释放mLooperLock
并等待mLooperCondition
。
所以,要让这场比赛发生,它必须像这样发生:
- 其他线程获取
mLooperLock
。 - 其他线程检查
mExitRequested
,它false
。 - 我们将
mExitRequested
设置为true
。 - 我们称
mLooperCondition.notify_all
. - 其他线程等待
mLooperCondition
,释放mLooperLock
。 - 哎呀。等待已经发生的通知。
因此,如果我们将代码更改为:
mExitRequested.store(true);
{ std::lock_guard<Mutex> lock(mLooperLock); }
mLooperCondition.notify_all();
这确保了没有其他线程可以检查mExitRequested
并查看false
,然后等待mLooperCondition
。因为另一个线程必须在整个过程中保持mLooperLock
锁,这不会发生,因为我们是在该过程中间获得它的。
再试一次:
- 其他线程获取
mLooperLock
。 - 其他线程检查
mExitRequested
,false
。 - 我们将
mExitRequested
设置为true
。 - 通过获取和释放
nLooperLock
,在另一个线程释放mLooperLock
之前,我们不会有任何向前的进展。 - 我们称
mLooperCondition.notify_all
.
现在,要么其他线程在该条件下阻塞,要么不阻塞。如果没有,那就没有问题了。如果是这样,仍然没有问题,因为mLooperLock
的解锁是条件变量的原子"解锁和等待"操作,保证它看到我们的通知。
相关文章:
- 库函数需要一个 std::function<void(void)>,如何传入类函数?
- 将 std::array 移动到另一个 std::array
- 为什么 Clang std::ostream 写一个 std::istream 无法读取的双精度?
- 访问 std:vector 的类成员 std:vector 在一个类中与另一个 std:vector
- 是否有一个 std::set 函数来确定不超过数字 x 的最大元素?
- 除了 std::vector 之外,是否有一个 std 容器不会复制和销毁作为类的元素?
- 我正在将一个 std::string 传递给一个 boost 函数,该函数对该类型进行常量引用,但该值发生了变化
- 在线程 A 中创建一个 std::thread 对象,在线程 B 中连接
- 使用 glDrawElements 绘制一个 std::vector
- 从 C 字符串构造 std::string 与从另一个 std::string 构造 std::string 不一致
- 我可以得到一个字符 * 到一个 std::sregex_iterator 匹配 str() 吗?
- 如何有效地将(一些)项目从一个std::map移动到另一个std::map
- 我可以制作一个std::set的constexpr对象吗
- 打印一个带有静态 int 的函数,有一个 std::cout 和多个 std::cout 有什么区别?
- Visual Studio 2017 STL 可视化工具失败了一个 std::map<MyIntrusivePtr, std::tuple<....> >
- 如何设置一个 std::vector 与另一个,其中两个是不同类的向量?
- SWIG:传递一个 std::vector< std::vector <double> >指向 python 的指针
- 如何构造一个 std::variant 类型对象,其自身 Templated 和构造函数转发参数
- 如何声明一个 std::用不同值内联初始化的结构数组
- 将对象从一个 std::d eque 移动到另一个的更好方法