简体   繁体   English

std::condition_variable 内存写入可见性

[英]std::condition_variable memory writes visibility

Does std::condition_variable::notify_one() or std::condition_variable::notify_all() guarantee that non-atomic memory writes in the current thread prior to the call will be visible in notified threads? std::condition_variable::notify_one()std::condition_variable::notify_all()保证在调用之前当前线程中的非原子内存写入在通知线程中可见?

Other threads do:其他线程做:

{
    std::unique_lock lock(mutex);
    cv.wait(lock, []() { return values[threadIndex] != 0; });
    // May a thread here see a zero value and therefore start to wait again?
}

Main thread does:主线程执行:

fillData(values); // All values are zero and all threads wait() before calling this.
cv.notify_all(); // Do need some memory fence or lock before this
                 // to ensure that new non-zero values will be visible
                 // in other threads immediately after waking up?

Doesn't notify_all() store some atomic value therefore enforcing memory ordering? notify_all() 不存储一些原子值从而强制执行内存排序吗? I did not clarified it.我没有澄清。

UPD : according to Superlokkus' answer and an answer here : we have to acquire a lock to ensure memory writes visibility in other threads (memory propagation), otherwise threads in my case may read zero values. UPD :根据 Superlokkus 的回答和这里的回答:我们必须获取一个锁以确保其他线程中的内存写入可见性(内存传播),否则在我的情况下线程可能会读取零值。

Also I missed this quote here about condition_variable , which specifically answers my question.我也错过了这里关于 condition_variable 的引用,它专门回答了我的问题。 Even an atomic variable has to be modified under a lock in a case when the modification must become visible immediately.在修改必须立即可见的情况下,甚至必须在锁下修改原子变量。

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 guess you are mixing up memory ordering of so called atomic values and the mechanisms of classic lock based synchronization.我猜您正在混淆所谓的原子值的内存排序和基于锁的经典同步机制。

When you have a datum which is shared between threads, lets say an int for example, one thread can not simply read it while the other thread might be write to it meanwhile.当你有一个在线程之间共享的数据时,比如说一个int ,一个线程不能简单地读取它,而另一个线程可能同时写入它。 Otherwise we would have a data race.否则我们就会有数据竞赛。

To get around this for long time we used classic lock based synchronization: The threads share at least a mutex and the int .为了长时间解决这个问题,我们使用了经典的基于锁的同步:线程至少共享一个互斥锁和int To read or to write any thread has to hold the lock first, meaning they wait on the mutex.要读取或写入任何线程必须先持有锁,这意味着它们在互斥锁上等待。 Mutexes are build so that they are fine that this can happen concurrently.互斥体被构建,以便它们可以同时发生。 If a thread wins gettting the mutex it can change or read the int and then should unlock it, so others can read/write too.如果一个线程赢得互斥锁,它可以更改或读取int然后应该解锁它,这样其他人也可以读/写。 Using a conditional variable like you used is just to make the pattern "readers wait for a change of a value by a writer" more efficient, they get woken up by the cv instead of periodically waiting on the lock, reading, and unlocking, which would be called busy waiting.使用像您使用的条件变量只是为了使“读取器等待写入器更改值”的模式更有效,它们会被 cv 唤醒,而不是定期等待锁定、读取和解锁,这会被称为忙等待。

So because you hold the lock in any after waiting on the mutex or in you case, correctly (mutex is still needed) waiting on the conditional variable, you can change the int .因此,因为在等待互斥锁之后或在您的情况下,正确地(仍然需要互斥锁)等待条件变量后,您将锁定保持在任何位置,您可以更改int And readers will read the new value after the writer was able to wrote it, never the old.读者将在作者能够写出新值后读取新值,而不是旧值。 UPDATE : However one thing if have to add, which might also be the cause of confusion: Conditional variables are subject for so called spurious wakeups.更新:但是,如果必须添加一件事,这也可能是混淆的原因:条件变量受所谓的虚假唤醒的影响。 Meaning even though you write did not have notified any thread, a read thread might still wake up, with the mutex locked.这意味着即使您写入没有通知任何线程,读取线程可能仍会唤醒,互斥锁被锁定。 So you have to check if you writer actually waked you up, which is usually done by the writer by changing another datum just to notify this, or if its suitable by using the same datum you already wanted to share.因此,您必须检查您的作者是否真的唤醒了您,这通常是由作者通过更改另一个数据来通知这一点来完成的,或者通过使用您已经想共享的相同数据是否合适。 The lambda parameter overload of std::condition_variable::wait was just made to make the checking and going back to sleep code looking a bit prettier. std::condition_variable::wait的 lambda 参数重载只是为了使检查和返回睡眠代码看起来更漂亮一些。 Based on your question now I don't know if you want to use you values for this job.根据你现在的问题,我不知道你是否想在这份工作中使用你的values

However at snippet for the "main" thread is incorrect or incomplete: You are not synchronizing on the mutex in order to change values .但是,“主”线程的代码片段不正确或不完整:您没有在互斥锁上进行同步以更改values You have to hold the lock for that, but notifying can be done without the lock.您必须为此持有锁,但可以在没有锁的情况下进行通知。

std::unique_lock lock(mutex);
fillData(values); 
lock.unlock();
cv.notify_all(); 

But these mutex based patters have some drawbacks and are slow, only one thread at a time can do something.但是这些基于互斥锁的模式有一些缺点并且速度很慢,一次只有一个线程可以做某事。 This is were so called atomics, like std::atomic<int> came into play.这就是所谓的原子,就像std::atomic<int>发挥作用。 They can be written and read at the same time without an mutex by multiple threads concurrently.它们可以同时被多个线程同时写入和读取,而无需互斥锁。 Memory ordering is only a thing to consider there and an optimization for cases where you uses several of them in a meaningful way or you don't need the "after the write, I never see the old value" guarantee.内存排序只是一个需要考虑的事情,并且对于您以有意义的方式使用其中几个或者您不需要“写入后,我永远不会看到旧值”保证的情况进行优化。 However with it's default memory ordering memory_order_seq_cst you would also be fine.但是,使用它的默认内存排序memory_order_seq_cst您也可以。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM