http://en.cppreference.com/w/cpp/atomic/memory_order和其他C++ 11在线参考,将memory_order_acquire和memory_order_release定义为:
这似乎允许在获取操作之前执行获取后写入,这看起来很奇怪(通常的获取/释放操作语义限制所有内存操作的移动).
相同的在线资源(http://en.cppreference.com/w/cpp/atomic/atomic_flag)表明可以使用C++原子和上面提到的宽松内存排序规则构建自旋锁互斥:
lock mutex: while (lock.test_and_set(std::memory_order_acquire))
unlock mutex: lock.clear(std::memory_order_release);
Run Code Online (Sandbox Code Playgroud)
有了这个锁定/解锁的定义,如果确实以这种方式定义了memory_order_acquire/release,那么下面的简单代码就不会被破坏(即,不禁止对获取后写入进行重新排序):
Thread1:
(0) lock
(1) x = 1;
(2) if (x != 1) PANIC
(3) unlock
Thread2:
(4) lock
(5) x = 0;
(6) unlock
Run Code Online (Sandbox Code Playgroud)
以下执行是否可行:(0)锁定,(1)x = 1,(5)x = 0,(2)PANIC?我错过了什么?
在C ++草案标准中多次使用了“在……之前发生”一词。
例如:终止 [basic.start.term] / 5
如果在调用std?::?atexit之前强烈完成了具有静态存储持续时间的对象的初始化(请参见[support.start.term]),则对该函数的调用将传递给std?::?atexit。在调用对象的析构函数之前进行排序。如果对std?::?atexit的调用很强地发生在具有静态存储持续时间的对象的初始化完成之前,则在传递给std?::?atexit的函数的调用之前对对象的析构函数的调用进行排序。 。如果对std?::?atexit的调用在另一次对std?::?atexit的调用之前强烈发生,则传递给第二个std?::?atexit调用的函数的调用将在传递给函数的函数的调用之前按顺序进行。第一个std?::?atexit调用。
并在 数据竞赛 [intro.races] / 12中定义
评估A发生在评估D之前,如果发生以下情况之一
(12.1)A在D之前排序,或
(12.2)A与D同步,并且A和D都是顺序一致的原子操作([atomics.order]),或者
(12.3)对B和C进行求值,使得A在B之前排序,B恰好在C之前发生,而C在D之前排序,或者
(12.4)有一个评估B,使得A强烈发生在B之前,而B强烈发生在D之前。
[注意:非正式地,如果A强烈地发生在B之前,那么在所有情况下A似乎都在B之前被评估。强烈发生在排除消耗操作之前。—尾注]
为什么引入“强烈发生”?直觉上,它与“之前发生的事情”有什么区别和关系?
注释中的“在所有情况下A似乎都在B之前被评估”是什么意思?
(注意:此问题的动机是Peter Cordes在此答案下的评论。)
标准报价附加草案(感谢Peter Cordes)
满足以下约束的所有memory_order?::?seq_cst操作(包括栅栏)上只有一个总顺序S。首先,如果A和B是memory_order?::?seq_cst运算,并且A强烈地发生在B之前,那么A在S中先于B。其次,对于对象M上的每对原子操作A和B,A的相干性在B之前,S必须满足以下四个条件:
(4.1)如果A和B都是memory_order?::?seq_cst操作,则A在S中先于B;和
(4.2)如果A是一个memory_order?::?seq_cst操作,而B发生在memory_order?::?seq_cst栅栏Y之前,则A在S中位于Y之前;和
(4.3)如果memory_order?::?seq_cst栅栏X发生在A之前,而B是memory_order?::?seq_cst操作,则X在S之前位于B之前;和
(4.4)如果memory_order?::?seq_cst防护栏X发生在A之前,而B发生在memory_order?::?seq_cst防护栏Y之前,则X在S中位于Y之前。
c++ multithreading memory-model language-lawyer happens-before
来自《Effective Java》一书:
虽然 volatile 修饰符不执行互斥,但它保证任何读取该字段的线程都会看到最近写入的值
SO和许多其他来源声称类似的事情。
这是真的?
我的意思是真的,不是一个足够接近的模型,或者只在 x86 上,或者只在 Oracle JVM 中,或者“最近写的”的一些定义,这不是标准的英语解释......
其他来源(SO 示例)说 Java 中的 volatile 就像 C++ 中的获取/释放语义。我认为不提供报价的保证。
我发现在JLS 17.4.4 中它说“对 volatile 变量 v 的写入(第 8.3.1.4 节)与任何线程对 v 的所有后续读取同步(其中“后续”是根据同步顺序定义的)。 ” 但我不太明白。
有很多支持和反对的消息来源,所以我希望答案能够说服其中许多(在任何一方)确实是错误的 - 例如参考或规范,或反示例代码。
与我之前的问题类似,请考虑以下代码
-- Initially --
std::atomic<int> x{0};
std::atomic<int> y{0};
-- Thread 1 --
x.store(1, std::memory_order_release);
-- Thread 2 --
y.store(2, std::memory_order_release);
-- Thread 3 --
int r1 = x.load(std::memory_order_acquire); // x first
int r2 = y.load(std::memory_order_acquire);
-- Thread 4 --
int r3 = y.load(std::memory_order_acquire); // y first
int r4 = x.load(std::memory_order_acquire);
Run Code Online (Sandbox Code Playgroud)
是怪异的结果 r1==1, r2==0,并r3==2, r4==0有可能在C ++ 11内存模型下,这种情况下?如果我要全部替换std::memory_order_acq_rel成该std::memory_order_relaxed怎么办?
在x86上,这样的结果似乎是被禁止的,请参见此SO问题,但我一般是在询问C ++ 11内存模型。
奖励问题:
我们都同意,与std::memory_order_seq_cst该怪异的结果不会在C ++ 11被允许。现在,赫伯·萨特(Herb Sutter)在他著名的- …
#include <thread>
#include <atomic>
#include <cassert>
std::atomic<bool> x = {false};
std::atomic<bool> y = {false};
std::atomic<int> z = {0};
void write_x()
{
x.store(true, std::memory_order_seq_cst); // #1
}
void write_y()
{
y.store(true, std::memory_order_seq_cst); // #2
}
void read_x_then_y()
{
while (!x.load(std::memory_order_seq_cst)) // #3
;
if (y.load(std::memory_order_seq_cst)) { // #4
++z;
}
}
void read_y_then_x()
{
while (!y.load(std::memory_order_seq_cst)) // #5
;
if (x.load(std::memory_order_seq_cst)) { // #6
++z;
}
}
int main()
{
std::thread a(write_x);
std::thread b(write_y);
std::thread c(read_x_then_y);
std::thread d(read_y_then_x); …Run Code Online (Sandbox Code Playgroud)