通知线程是否始终需要在修改期间锁定共享数据?

时间:2017-11-17 17:44:45

标签: c++ multithreading c++11 mutex condition-variable

使用条件变量时,http://en.cppreference.com/w/cpp/thread/condition_variable描述了通知的线程的典型步骤:

  
      
  1. 获取std :: mutex(通常通过std :: lock_guard)
  2.   
  3. 在锁定时执行修改
  4.   
  5. 在std :: condition_variable上执行notify_one或notify_all(不需要保留锁通知)
  6.   

对于下面显示的简单情况,主线程在修改它时是否需要锁定“停止”?虽然我理解在修改共享数据时锁定几乎总是一个好主意,但我不确定为什么在这种情况下它是必要的。

std::condition_variable cv;
std::mutex mutex;
bool stop = false;

void worker() {
    std::unique_lock<std::mutex> lock(mutex);
    cv.wait(lock, [] { return stop; })
}

// No lock (Why would this not work?)
void main() {
     std::thread(worker);
     std::this_thread::sleep_for(1s);
     stop = true;
     cv.notify_one();
}

// With lock: why is this neccesary?
void main() {
     std::thread mythread(worker);
     std::this_thread::sleep_for(1s);
     {
         std::unique_lock<std::mutex>(mutex);
         stop = true;
     }
     cv.notify_one();
     mythread.join();
}

2 个答案:

答案 0 :(得分:6)

  

对于下面显示的简单情况,主线程必须锁定&#34;停止&#34;当它修改它?

是的,以防止竞争。除了可以通过this.state.isOn修复来自不同线程的共享数据的问题之外,想象一下这个事件的顺序:

std::atomic

在这种情况下,工作线程可能会永远睡眠,它会错过worker_thread: checks value of `stop` it is false main_thread: sets `stop` to true and sends signal worker_thread: sleeps on condition variable 设置为stop的事件,因为唤醒信号已经发送并且错过了它。

需要在修改共享数据时获取互斥量,因为只有这样才能处理检查条件并在工作线程中作为整体原子操作进行休眠或作用。

答案 1 :(得分:1)

是的,是必要的。
首先,从标准的角度来看,代码表现出未定义的行为,因为stop不是原子的,并且在锁定下不会发生变化,而其他线程可能会读取或写入它。如果多个线程读写共享内存,则读取和写入都必须在锁定下进行。

从硬件角度来看,如果stop未在锁定下更改,则其他线程(即使通过锁定某些锁定)也不能保证“看到”已完成的更改。锁不只是阻止其他线程进行干预,它们还强制将最新的更改读取或写入主内存,因此所有线程都可以使用最新的值。