通知线程是否总是需要在修改期间锁定共享数据?
Is it always necessary for a notifying thread to lock the shared data during modification?
使用条件变量时,http://en.cppreference.com/w/cpp/thread/condition_variable描述了通知线程的典型步骤:
- acquire a std::mutex (typically via std::lock_guard)
- perform the modification while the lock is held
- execute notify_one or notify_all on the std::condition_variable (the lock does not need to be held for notification)
下面这个简单的例子,主线程修改的时候需要加锁"stop"吗?虽然我知道在修改共享数据时锁定几乎总是一个好主意,但我不确定为什么在这种情况下有必要。
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();
}
For the simple case shown below, is it necessary for the main thread to lock "stop" when it modifies it?
是的,以防止竞争条件。除了可以通过 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
设置为 true
的事件,这仅仅是因为唤醒信号已经发送并且它错过了它。
需要在修改共享数据时获取互斥量,因为只有这样您才能将检查条件和进入休眠状态或在工作线程中对其进行操作作为整体原子操作来处理。
是,是必须的。
首先,从标准的角度来看,您的代码表现出未定义的行为,因为 stop
不是原子的并且不会在锁定下更改,而其他线程可能会读取或写入它。
如果多个线程读写共享内存,则读写都必须在锁下进行。
从硬件的角度来看,如果 stop
未在锁下更改,则其他线程即使锁定了某个锁,也不能保证 "see" 已完成的更改。锁不仅防止其他线程干预,它们还强制将最新更改读取或写入主内存,因此所有线程都可以使用最新值。
使用条件变量时,http://en.cppreference.com/w/cpp/thread/condition_variable描述了通知线程的典型步骤:
- acquire a std::mutex (typically via std::lock_guard)
- perform the modification while the lock is held
- execute notify_one or notify_all on the std::condition_variable (the lock does not need to be held for notification)
下面这个简单的例子,主线程修改的时候需要加锁"stop"吗?虽然我知道在修改共享数据时锁定几乎总是一个好主意,但我不确定为什么在这种情况下有必要。
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();
}
For the simple case shown below, is it necessary for the main thread to lock "stop" when it modifies it?
是的,以防止竞争条件。除了可以通过 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
设置为 true
的事件,这仅仅是因为唤醒信号已经发送并且它错过了它。
需要在修改共享数据时获取互斥量,因为只有这样您才能将检查条件和进入休眠状态或在工作线程中对其进行操作作为整体原子操作来处理。
是,是必须的。
首先,从标准的角度来看,您的代码表现出未定义的行为,因为 stop
不是原子的并且不会在锁定下更改,而其他线程可能会读取或写入它。
如果多个线程读写共享内存,则读写都必须在锁下进行。
从硬件的角度来看,如果 stop
未在锁下更改,则其他线程即使锁定了某个锁,也不能保证 "see" 已完成的更改。锁不仅防止其他线程干预,它们还强制将最新更改读取或写入主内存,因此所有线程都可以使用最新值。