我正在研究这个网站:https://gcc.gnu.org/wiki/Atomic/GCCMM/AtomicSync ,这对理解关于原子类的话题非常有帮助。
但是这个放松模式的例子很难理解:
/*Thread 1:*/
y.store (20, memory_order_relaxed)
x.store (10, memory_order_relaxed)
/*Thread 2*/
if (x.load (memory_order_relaxed) == 10)
{
assert (y.load(memory_order_relaxed) == 20) /* assert A */
y.store (10, memory_order_relaxed)
}
/*Thread 3*/
if (y.load (memory_order_relaxed) == 10)
assert (x.load(memory_order_relaxed) == 10) /* assert B */
对我来说断言 B 永远不会失败,因为 x 必须是 10 并且 y=10 因为线程 2 已经以此为条件。
但是网站上说这个例子中的任何一个断言实际上都可能失败。
最佳答案
To me assert B should never fail, since x must be 10 and y=10 because of thread 2 has conditioned on this.
实际上,您的论点是因为在线程 2 中将 10 存储到 x
发生在将 10 存储到 y
之前,因此在线程 3 中必须是情况。
但是,由于您仅使用宽松的内存操作,因此代码中没有任何内容需要两个不同的线程就不同 变量的修改之间的顺序达成一致。因此,实际上线程 2 可能会先将 10 存储到 x
,然后再将 10 存储到 y
,而线程 3 会以相反的顺序看到这两个操作。
为了确保断言 B 成功,您实际上需要确保当线程 3 看到 y
的值 10 时,它还会看到该线程执行的任何其他副作用在存储时间之前将 10 存储到 y
中。也就是说,您需要将 10 存储到 y
中以同步 从 y
加载 10。这可以通过让存储执行释放并让负载执行获取来完成:
// thread 2
y.store (10, memory_order_release);
// thread 3
if (y.load (memory_order_acquire) == 10)
释放操作与读取存储值的获取操作同步。现在因为线程 2 中的存储与线程 3 中的加载同步,线程 3 中加载之后发生的任何事情都会看到线程 2 中存储之前发生的任何事情的副作用。因此断言将成功。
当然,我们还需要确保断言A成功,方法是让线程1中的x.store
使用release,线程2中的x.load
使用获取。
关于c++ - 对原子类 : memory_order_relaxed 感到困惑,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/46065770/