在condition_variable::notify_all() 之后或之前解锁互斥锁?

Kar*_*ari 8 c++ multithreading condition-variable

查看几个视频文档示例,我们调用notify_all(). 之后调用会更好吗?

常见的方式:

通知程序线程内部:

//prepare data for several worker-threads;

//and now, awaken the threads:
std::unique_lock<std::mutex> lock2(sharedMutex);
_threadsCanAwaken = true;

lock2.unlock(); 
_conditionVar.notify_all(); //awaken all the worker threads;

//wait until all threads completed;

//cleanup:
_threadsCanAwaken = false;

//prepare new batches once again, etc, etc
Run Code Online (Sandbox Code Playgroud)

在工作线程之一内部:

while(true){
    // wait for the next batch:

    std::unique_lock<std::mutex> lock1(sharedMutex);
    _conditionVar.wait(lock1,  [](){return _threadsCanAwaken});
    lock1.unlock(); //let sibling worker-threads work on their part as well

    //perform the final task

    //signal the notifier that one more thread has completed;

    //loop back and wait until the next task
}
Run Code Online (Sandbox Code Playgroud)

lock2请注意,在我们通知条件变量之前如何解锁 - 我们是否应该在 后解锁它notify_all()

编辑

从我下面的评论来看:我担心的是,如果工作人员虚假地醒来,看到互斥锁已解锁,超快速地完成任务并循环回到 while 的开头,该怎么办?现在,慢速通知程序最终调用了notify_all(),导致工作程序循环了额外的时间(过多且不受欢迎)。

Dav*_*rtz 8

除非您的实现不寻常,否则在发送条件变量信号之前解锁互斥体没有任何优势。在发出信号之前解锁有两个缺点:

  1. 如果您在发出信号之前解锁,则信号可能会唤醒在您解锁后选择在条件变量上阻塞的线程。如果您使用相同的条件变量来表示多个逻辑条件,则可能会导致死锁。这种错误很难创建、很难诊断、也很难理解。通过在解锁之前始终发出信号可以轻松避免这种情况。这确保了共享状态和信号的更改是原子操作,并且不可能出现竞争条件和死锁。

  2. 在发信号之前解锁会带来性能损失,而在发信号之后解锁可以避免这种性能损失。如果您在解锁之前发出信号,则良好的实现将知道您的信号不可能使任何线程准备好运行,因为互斥体由调用线程持有,并且受条件变量影响的任何线程在没有互斥体的情况下必然无法向前推进。这允许显着的优化(通常称为“等待变形”),如果您先解锁,则这是不可能的。

因此,除非有特殊原因,否则请在保持锁定状态时发出信号。