我目前正在研究Google的细丝工作系统。您可以找到源代码here。令我感到困惑的部分是这个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();
}
我很困惑为什么即使在锁定和解锁之间没有任何动作也需要锁定和解锁。在任何情况下都需要这种空的锁定和解锁吗?
答案 0 :(得分:8)
这有点hack。首先,让我们看一下没有该代码的代码:
mExitRequested.store(true);
mLooperCondition.notify_all();
这里可能存在比赛条件。其他一些代码可能已经注意到mExitRequested
是错误的,并在我们调用mLooperCondition
之后立即开始等待notify_all
。
比赛将是:
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
的解锁是条件变量的原子“解锁并等待”操作,保证它能看到我们的通知。