C++11 具有非原子变量的原子内存顺序
C++11 Atomic memory order with non-atomic variables
我不确定 c++11 中原子变量的内存排序保证如何影响对其他内存的操作。
假设我有一个线程定期调用写入函数来更新值,另一个线程调用读取函数来获取当前值。是否保证d = value;
的效果不会在a = version;
的效果之前看到,而会在b = version;
的效果之前看到?
atomic<int> a {0};
atomic<int> b {0};
double d;
void write(int version, double value) {
a = version;
d = value;
b = version;
}
double read() {
int x,y;
double ret;
do {
x = b;
ret = d;
y = a;
} while (x != y);
return ret;
}
您的对象 d
由两个线程写入和读取,它不是原子的。这是不安全的,正如 C++ 多线程标准中所建议的那样:
1.10/4 Two expression evaluations conflict if one of them modifies a memory location and the other one accesses or modifies the same memory location.
1.10/21 The execution of a program contains a data race if it contains two conflicting actions in different threads,at least one of
which is not atomic, and neither happens before the other. Any such
data race results in undefined behavior.
重要编辑:
在您的非原子情况下,您无法保证读取和写入之间的顺序。您甚至无法保证 reader 将读取作者写入的值(此 short article explains the risk 用于非原子变量)。
尽管如此,你的 reader 的循环基于对周围原子变量的测试完成,为此是有力的保证。假设 version
从不在 writer 不同的调用之间重复,并且给定您获取它们的值的相反顺序:
- 如果两个原子相等,
d
读取的顺序与 d
写入的顺序相比不会很不幸。
- 同样,如果两个原子相等,则读取值不能不一致。
这意味着如果您的非原子出现不利的竞争条件,多亏了循环,您最终会阅读最后一个 value
。
规则是,给定一个执行一次的 write
线程,并且没有其他修改 a
、b
或 d
、
- 您可以随时从不同的线程阅读
a
和 b
,并且
- 如果您阅读
b
并看到 version
存储在其中,那么
- 您可以阅读
d
;和
- 您阅读的内容将是
value
。
注意第二部分是否为真取决于内存排序;默认值 (memory_order_seq_cst
) 也是如此。
Is it guaranteed that the effects of d = value;
will not be seen before effects of a = version;
, and will be seen before the effects of b = version;
?
是的,是的。这是因为在读取或写入 atomic<>
变量时隐含了 顺序一致性屏障 。
不是将 version
标记存储到 two 原子变量之前和之后,您可以递增 single atomic修改前后变量:
atomic<int> a = {0};
double d;
void write(double value)
{
a = a + 1; // 'a' become odd
d = value; //or other modification of protected value(s)
a = a + 1; // 'a' become even, but not equal to the one before modification
}
double read(void)
{
int x;
double ret;
do
{
x = a;
ret = value; // or other action with protected value(s)
} while((x & 2) || (x != a));
return ret;
}
这在 Linux 内核中称为 seqlock:http://en.wikipedia.org/wiki/Seqlock
我不确定 c++11 中原子变量的内存排序保证如何影响对其他内存的操作。
假设我有一个线程定期调用写入函数来更新值,另一个线程调用读取函数来获取当前值。是否保证d = value;
的效果不会在a = version;
的效果之前看到,而会在b = version;
的效果之前看到?
atomic<int> a {0};
atomic<int> b {0};
double d;
void write(int version, double value) {
a = version;
d = value;
b = version;
}
double read() {
int x,y;
double ret;
do {
x = b;
ret = d;
y = a;
} while (x != y);
return ret;
}
您的对象 d
由两个线程写入和读取,它不是原子的。这是不安全的,正如 C++ 多线程标准中所建议的那样:
1.10/4 Two expression evaluations conflict if one of them modifies a memory location and the other one accesses or modifies the same memory location.
1.10/21 The execution of a program contains a data race if it contains two conflicting actions in different threads,at least one of which is not atomic, and neither happens before the other. Any such data race results in undefined behavior.
重要编辑:
在您的非原子情况下,您无法保证读取和写入之间的顺序。您甚至无法保证 reader 将读取作者写入的值(此 short article explains the risk 用于非原子变量)。
尽管如此,你的 reader 的循环基于对周围原子变量的测试完成,为此是有力的保证。假设 version
从不在 writer 不同的调用之间重复,并且给定您获取它们的值的相反顺序:
- 如果两个原子相等,
d
读取的顺序与d
写入的顺序相比不会很不幸。 - 同样,如果两个原子相等,则读取值不能不一致。
这意味着如果您的非原子出现不利的竞争条件,多亏了循环,您最终会阅读最后一个 value
。
规则是,给定一个执行一次的 write
线程,并且没有其他修改 a
、b
或 d
、
- 您可以随时从不同的线程阅读
a
和b
,并且 - 如果您阅读
b
并看到version
存储在其中,那么- 您可以阅读
d
;和 - 您阅读的内容将是
value
。
- 您可以阅读
注意第二部分是否为真取决于内存排序;默认值 (memory_order_seq_cst
) 也是如此。
Is it guaranteed that the effects of
d = value;
will not be seen before effects ofa = version;
, and will be seen before the effects ofb = version;
?
是的,是的。这是因为在读取或写入 atomic<>
变量时隐含了 顺序一致性屏障 。
不是将 version
标记存储到 two 原子变量之前和之后,您可以递增 single atomic修改前后变量:
atomic<int> a = {0};
double d;
void write(double value)
{
a = a + 1; // 'a' become odd
d = value; //or other modification of protected value(s)
a = a + 1; // 'a' become even, but not equal to the one before modification
}
double read(void)
{
int x;
double ret;
do
{
x = a;
ret = value; // or other action with protected value(s)
} while((x & 2) || (x != a));
return ret;
}
这在 Linux 内核中称为 seqlock:http://en.wikipedia.org/wiki/Seqlock