为什么我需要获得一个锁才能修改共享的“原子”密码?通知condition_variable之前的变量 [英] Why do I need to acquire a lock to modify a shared "atomic" variable before notifying condition_variable
问题描述
编码为 cppreference.com :
打算修改变量的线程必须
The thread that intends to modify the variable has to
- 获取标准: :mutex(通常通过std :: lock_guard)
- 在持有锁的同时执行修改
- 在std :: condition_variable上执行notify_one或notify_all (不必为通知而持有该锁)
即使共享变量是原子变量,也必须在互斥锁下对其进行修改。为了正确地将修改发布到等待线程。
Even if the shared variable is atomic, it must be modified under the mutex in order to correctly publish the modification to the waiting thread.
我不太了解,为什么修改原子变量需要一把锁。请参见以下代码段:
I'm not quite understand, why modifying a atomic variable need to require an lock. Please see following code snippet:
static std::atomic_bool s_run {true};
static std::atomic_bool s_hasEvent {false};
static std::mutex s_mtx;
static std::condition_variabel s_cv;
// Thread A - the consumer thread
function threadA()
{
while (s_run)
{
{
std::unique_lock<std::mutex> lock(s_mtx);
s_cv.wait(lock, [this]{
return m_hasEvents.load(std::memory_order_relaxed);
});
}
// process event
event = lockfree_queue.pop();
..... code to process the event ....
}
}
// Thread B - publisher thread
function PushEvent(event)
{
lockfree_queque.push(event)
s_hasEvent.store(true, std::memory_order_release);
s_cv.notify_one();
}
在PushEvent函数中,我不获取s_mtx,因为s_hasEvent是一个原子变量并且队列是无锁的。不获取s_mtx锁是什么问题?
In the PushEvent function, I do not acquire s_mtx because s_hasEvent is an atomic variable and the queue is lockfree. What is the problem w/o acquire the s_mtx lock?
推荐答案
如 Yakk对您所链接问题的回答是为了防止发生导致错过的唤醒事件的一系列事件:
As noted in Yakk's answer to the question you linked to it is to protect against this sequence of events causing a missed wake-up:
- 线程A锁定互斥量。
- 线程A调用lambda的闭包,该闭包执行
m_hasEvents.load(std :: memory_order_relaxed);
并返回值false
。
- Thread A calls the lambda's closure which does
m_hasEvents.load(std::memory_order_relaxed);
and returns the valuefalse
.
- 线程A被调度程序中断,线程B开始执行跑。
- 线程B将事件推送到队列中并存储到
s_hasEvent
- Thread B pushes an event into the queue and stores to
s_hasEvent
- 线程B运行
s_cv.notify_one()
。
- Thread B runs
s_cv.notify_one()
.
- 线程B被调度程序中断,线程A再次运行。
- 线程A评估
false
结果由闭包返回,确定没有挂起的事件。
- Thread A evaluates the
false
result returned by the closure, deciding there are no pending events.
- 线程A在条件变量上阻塞,等待事件。
这意味着 notify_one()
呼叫已丢失,即使队列中已准备好事件,条件变量也会阻塞。
This means the notify_one()
call has been missed, and the condition variable will block even though there is an event ready in the queue.
如果已完成对共享变量的更新当互斥锁被锁定时,步骤4和步骤2之间就不可能发生步骤4,因此条件变量的事件检查将获得一致的结果。使用发布者和消费者使用的互斥锁,存储到 s_hasEvent
的商店将发生在步骤1之前(因此,闭包将加载值 true
并且永远不会阻塞条件变量),否则它会在第8步之后发生(因此 notify_one()
调用会唤醒它)。
If the update to the shared variable is done while the mutex is locked then it's not possible for the step 4 to happen between steps 2 and 7, so the condition variable's check for events gets a consistent result. With a mutex used by the publisher and the consumer either the store to s_hasEvent
happens before step 1 (and so the closure loads the value true
and never blocks on the condition variable) or it happens after step 8 (and so the notify_one()
call will wake it up).
这篇关于为什么我需要获得一个锁才能修改共享的“原子”密码?通知condition_variable之前的变量的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!