Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/cplusplus/125.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/multithreading/4.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ 为什么std::condition_变量的notify和wait函数都需要锁定的互斥锁_C++_Multithreading_C++11_Locking_Mutex - Fatal编程技术网

C++ 为什么std::condition_变量的notify和wait函数都需要锁定的互斥锁

C++ 为什么std::condition_变量的notify和wait函数都需要锁定的互斥锁,c++,multithreading,c++11,locking,mutex,C++,Multithreading,C++11,Locking,Mutex,在我对理解std::contion\u变量的不懈追求中,我遇到了以下问题。上面写着: void print_id (int id) { std::unique_lock<std::mutex> lck(mtx); while (!ready) cv.wait(lck); // ... std::cout << "thread " << id << '\n'; } void打印id(int id){ 标准:唯一锁定lck(mtx);

在我对理解
std::contion\u变量的不懈追求中,我遇到了以下问题。上面写着:

void print_id (int id) {
  std::unique_lock<std::mutex> lck(mtx);
  while (!ready) cv.wait(lck);
  // ...
  std::cout << "thread " << id << '\n';
}
void打印id(int id){
标准:唯一锁定lck(mtx);
而(!ready)cv.wait(lck);
// ...

std::cout您缺少的是
wait
解锁互斥锁,然后等待
cv
上的信号

它会在返回前再次锁定互斥锁

您可以通过单击找到示例的页面来发现这一点:

在阻塞线程时,函数自动调用lck.unlock(),允许其他锁定的线程继续

一旦收到通知(由其他线程显式通知),该函数将取消阻止并调用lck.lock(),使lck保持与调用该函数时相同的状态


您错过了调用
wait()
解锁互斥锁的一点。线程以原子方式(释放互斥锁+进入睡眠)。然后,当被信号唤醒时,它尝试重新获取互斥锁(可能是阻塞);一旦获取互斥锁,它就可以继续


请注意,调用
notify\u*
时不必锁定互斥锁,只有
wait*

才能回答所提出的问题,这对于声称出于性能原因不应获得通知锁定似乎是必要的(正确性是否比性能更重要?):锁定“等待”的必要性和始终锁定“通知”的建议是为了保护用户自己及其程序不受数据和逻辑争用的影响。如果没有锁定“go”,您发布的程序将立即在“ready”上进行数据争用。但是,即使ready本身是同步的(例如原子的)您将有一个丢失通知的逻辑竞赛,因为如果没有“go”中的锁定,通知可能会在检查“ready”之后发生就在实际等待之前,等待线程可能会无限期地被阻塞。原子变量本身的同步不足以防止这种情况。这就是为什么helgrind会在通知未持有锁的情况下发出警告的原因。在一些边缘情况下,notif确实不需要互斥锁y、 在所有这些情况下,都需要事先进行双向同步,以便生产线程能够确定另一个线程已经在等待。在我看来,这些情况仅供专家参考。实际上,我曾见过一位专家,在谈到多线程时,他认为一个原子计数器就足够了.这就是说,等待周围的锁对于正确性(或者,至少是等待的原子操作)始终是必需的,这就是为什么标准库强制执行它,并在进入等待时原子地解锁互斥锁的原因


与Windows事件不同,POSIX条件变量不是“防白痴”的,因为它们是无状态的(除了知道等待的线程之外)。建议在notify上使用锁是为了保护您免受最糟糕和最常见的错误。当然,如果您愿意,您可以使用mutex+condition var+bool变量构建类似Windows的有状态事件。

我认为print_id和go不应该使用同一个锁。锁的存在是为了只运行一个线程g print_id,只有一个线程运行go()。嗯,我检查过,生产者和消费者都使用同一个锁。奇怪。@Angew“保护条件变量本身”是什么意思?条件变量本身不是线程安全的吗?@laurisvr是的,这有点措词不当。收回。更像“它保护
cv+ready
对。”@Matthias Vegh cppreference.com强调在发出通知之前要始终解锁(主要是因为我相信并发在起作用)事实上,在向条件变量发送信号时保持锁可能是一种悲观的做法。调度程序可能会在您通知它时唤醒等待的线程,然后该线程无法获取锁,因为通知线程仍然保持着锁,这可能会导致线程立即生效!这会导致不必要的抖动。显然y调度程序可能与CV代码有关联,并且知道不唤醒等待的线程,除非它也可以立即获得互斥锁。注意:这与需要锁定互斥锁的pthreads_uCV相反notify@MikeVine至少Linux实现不要求在通知期间保持互斥。但是,您必须在发送通知之前锁定互斥锁,并且在更改等待条件之前(在本例中,
ready
)不能解锁互斥锁。例如,对于
mutex m
condition_变量v
atomic ready
,所有
m.lock();ready=true;c.notify_one();m.unlock();
m.lock();ready=true;m.unlock();c.notify_one();
ready=true;m.lock();m.unlock();c.notify_one();
已充分同步。在这种情况下,第三种可能是最有效的。通知不需要锁,但条件更改需要锁(确实使用原子将是一个错误)。啊,谢谢你澄清了一切:)。我的误解是因为我认为调用notify的函数可能无法通过互斥锁。但是,如果互斥锁已经通过等待解锁,当然可以。
void go() {
  std::unique_lock<std::mutex> lck(mtx);
  ready = true;
  cv.notify_all();
}