简体   繁体   English

为什么在进行条件变量notify之前我们需要一个空的std :: lock_guard?

[英]Why do we need an empty std::lock_guard before doing condition variable notify?

I am currently studying Google's Filament job system. 我目前正在研究Google的Filament工作系统。 You can find the source code here . 你可以在这里找到源代码。 The part that confuses me is this requestExit() method: 令我困惑的部分是这个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();
}

I am confused why we need to lock and unlock even though there is no action in between the lock and unlock. 我很困惑为什么我们需要锁定和解锁,即使锁和解锁之间没有动作。 Are there any cases where this empty lock and unlock is necessary? 是否有必要进行空锁和解锁?

This is a bit of a hack. 这有点像黑客。 First, let's look at the code without that: 首先,让我们看看没有它的代码:

mExitRequested.store(true);
mLooperCondition.notify_all();

There's a possible race condition here. 这里有可能的竞争条件。 Some other code might have noticed that mExitRequested was false and started waiting for mLooperCondition right after we called notify_all . 其他一些代码可能已经注意到mExitRequested为false并且在我们调用notify_all之后mLooperCondition开始等待notify_all

The race would be: 比赛将是:

  1. Other thread checks mExitRequested , it's false . 其他线程检查mExitRequested ,这是false
  2. We set mExitRequested to true . 我们将mExitRequested设置为true
  3. We call mLooperCondition.notify_all . 我们称之为mLooperCondition.notify_all
  4. Other thread waits for mLooperCondition . 其他线程等待mLooperCondition
  5. Oops. 哎呀。 Waiting for notify that already happened. 等待已经发生的通知。

But in order to wait for a condition variable, you must hold the associated mutex. 但是,为了等待条件变量,您必须保持关联的互斥锁。 So that can only happen if some other thread held the mLooperLock mutex. 所以只有当其他一些线程持有mLooperLock互斥锁时才会发生这种情况。 In fact, step 4 would really be: "Other thread releases mLooperLock and waits for mLooperCondition . 事实上,第4步确实是:“其他线程释放mLooperLock并等待mLooperCondition

So, for this race to happen, it must happen precisely like this: 因此,为了让这场比赛发生,它必须完全像这样:

  1. Other thread acquires mLooperLock . 其他线程获得mLooperLock
  2. Other thread checks mExitRequested , it's false . 其他线程检查mExitRequested ,这是false
  3. We set mExitRequested to true . 我们将mExitRequested设置为true
  4. We call mLooperCondition.notify_all . 我们称之为mLooperCondition.notify_all
  5. Other thread waits for mLooperCondition , releasing mLooperLock . 其他线程等待mLooperCondition ,释放mLooperLock
  6. Oops. 哎呀。 Waiting for notify that already happened. 等待已经发生的通知。

So, if we change the code to: 所以,如果我们将代码更改为:

mExitRequested.store(true);
{ std::lock_guard<Mutex> lock(mLooperLock); }
mLooperCondition.notify_all();

That ensures that no other thread could check mExitRequested and see false and then wait for mLooperCondition . 这确保没有其他线程可以检查mExitRequested并查看false然后等待mLooperCondition Because the other thread would have to hold the mLooperLock lock through the whole process, which can't happen since we acquired it in the middle of that process. 因为另一个线程必须在整个过程中保持mLooperLock锁定,这是因为我们在该过程的中间获取了它。

Trying it again: 再试一次:

  1. Other thread acquires mLooperLock . 其他线程获得mLooperLock
  2. Other thread checks mExitRequested , it's false . 其他线程检查mExitRequested ,这是false
  3. We set mExitRequested to true . 我们将mExitRequested设置为true
  4. By acquiring and releasing nLooperLock , we do not make any forward progress until the other thread releases mLooperLock . 通过获取和释放nLooperLock ,在另一个线程释放mLooperLock之前,我们不会进行任何前进。
  5. We call mLooperCondition.notify_all . 我们称之为mLooperCondition.notify_all

Now, either the other thread blocks on the condition or it doesn't. 现在,其他线程阻塞条件或不。 If it doesn't, there's no problem. 如果没有,那就没问题了。 If it does, there's still no problem because the unlocking of mLooperLock is the condition variable's atomic "unlock and wait" operation, guaranteeing that it sees our notify. 如果确实如此,那仍然没有问题,因为解锁mLooperLock是条件变量的原子“解锁和等待”操作,保证它看到我们的通知。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM