C++0x-MM和其他MMs之间的细微差别[英] Subtle difference between C++0x MM and other MMs

问题描述

考虑遵循 Wikipedia 中的 Peterson 算法实现:
http://en.wikipedia.org/wiki/Peterson%27s_algorithm

标志[0] = 0
标志[1] = 0
转 = 0

P0:标志[0] = 1
转=1
memory_barrier()
while( 标志[1] && 转 == 1 );
//什么都不做
//临界区
...
//临界区结束
标志[0] = 0

P1:标志[1] = 1
转=0
memory_barrier()
while(标志[0] &&转== 0);
//什么都不做
//临界区
...
//临界区结束
标志[1] = 0

我们可以使用 volatile 变量在 Java 中实现这一点,并且需要
memory_barrier() 将由编译器自动发出.
我们可以使用 volatile 变量在 C# 中实现这一点,并且
Thread.MemoryBarrier() 作为 memory_barrier().
我们可以在 x86 MM 中使用普通的加载和存储来实现这一点,并且
mfence 指令为 memory_barrier().
我们可以在 C++0x 中使用 std::atomic<并发出加载
来实现这一点使用 memory_order_acquire,使用 memory_order_release 存储,以及
atomic_thread_fence(memory_order_seq_cst) 作为 memory_barrier().这是
Java/C#/x86 实现最直接的翻译.

唯一的问题是C++0x的实现不行.
就我个人而言,这很违反直觉.并关注
问题出现.翻译一些现有的最简单的方法是什么
Java/C#/x86 算法实现到 C++0x?好像不是
这么简单……

Dmitriy V''jukov

推荐答案

8 月 24 日晚上 7:46*pm,"Dmitriy V''jukov"<dvyu...@gmail.com 写道:
考虑遵循 Wikipedia 中的 Peterson 算法实现:http://en.wikipedia.org/wiki/Peterson%27s_algorithm

*标志[0] * = 0
*标志[1] * = 0
*转* * *= 0

*P0: 标志[0] = 1
* * 转 = 1
* * memory_barrier()
* * while( flag[1] && turn == 1 );
* * * * * *//什么都不做
* *//临界区
* * ...
* *//临界区结束
* * 标志[0] = 0

P1:标志[1] = 1
* * 转 = 0
* * memory_barrier()
* * while(flag[0] && turn == 0 );
* * * * * *//什么都不做
* *//临界区
* * ...
* *//临界区结束
* * 标志[1] = 0

我们可以使用 volatile 变量在 Java 中实现这一点,并且需要
memory_barrier() 将由编译器自动发出.
而C++的等价物是使用seq_cst加载和存储,它们是
相当于 Java volatiles.
我们可以使用 volatile 变量在 C# 中实现这一点,并且
Thread.MemoryBarrier() 作为 memory_barrier().
我们可以在 x86 MM 中使用普通的加载和存储来实现这一点,并且
mfence 指令为 memory_barrier().
我们可以在 C++0x 中使用 std::atomic<并发出加载
来实现这一点使用 memory_order_acquire,使用 memory_order_release 存储,以及
atomic_thread_fence(memory_order_seq_cst) 作为 memory_barrier().这是
Java/C#/x86 实现最直接的翻译.

唯一的问题是 C++0x 实现不起作用.
为什么它不起作用?

24 á×,21:52,Peter Dimov <pdi...@gmail.com 写道:
8 月 24 日晚上 7:46,"Dmitriy V''jukov"<dvyu...@gmail.com 写道:
考虑遵循 Wikipedia 中的 Peterson 算法实现:http://en.wikipedia.org/wiki/Peterson%27s_algorithm
标志[0] = 0
标志[1] = 0
转 = 0
P0:标志[0] = 1
转=1
memory_barrier()
while( 标志[1] && 转 == 1 );
//什么都不做
//临界区
...
//临界区结束
标志[0] = 0
P1:标志[1] = 1
转=0
memory_barrier()
while(标志[0] &&转== 0);
//什么都不做
//临界区
...
//临界区结束
标志[1] = 0
我们可以使用 volatile 变量在 Java 中实现这一点,并且需要
memory_barrier() 将由编译器自动发出.

而C++的等价物是使用seq_cst加载和存储,它们是
相当于Java volatiles.

是的,可以实现任何依赖于
的算法C++0x 中使用 seq_cst atomic 的顺序一致内存模型
操作.但!seq_cst 原子操作,尤其是stores,可以
相当昂贵.因此,人们普遍希望使用较弱的操作,
比如 store-release 和 load-acquire.在 Java/C#/x86 中是可能的
使用弱操作 + 1 强实现彼得森算法
栅栏.在 C++0x 中 - 不是.

我们可以使用 volatile 变量在 C# 中实现这一点,并且
Thread.MemoryBarrier() 作为 memory_barrier().
我们可以在 x86 MM 中使用普通的加载和存储来实现这一点,并且
mfence 指令为 memory_barrier().
我们可以在 C++0x 中使用 std::atomic<并发出加载
来实现这一点使用 memory_order_acquire,使用 memory_order_release 存储,以及
atomic_thread_fence(memory_order_seq_cst) 作为 memory_barrier().这是
Java/C#/x86 实现的最直接的翻译.
唯一的问题是 C++0x 实现不起作用.

为什么它不起作用?

我的意思不是彼得森算法的每个 C++0x 实现,而是
使用 store-release、load-acquire + 1
的特定实现seq_cst 栅栏.
Dmitriy V''jukov

8 月 24 日晚上 9:44*pm,"Dmitriy V''jukov"<dvyu...@gmail.com 写道:
在 Java/C#/x86 中是可能的
使用弱操作 + 1 强实现彼得森算法
栅栏.在 C++0x 中 - 不是.
你会如何使用weak在Java中实现Peterson的算法
操作和围栏?Java 没有弱操作或栅栏.
它的 volatile 加载和存储等价于 C++MM 的 seq_cst 加载
和商店.两者都承诺顺序一致性(不多也不少).
我的意思不是彼得森算法的每个 C++0x 实现,而是
使用 store-release、load-acquire + 1
的特定实现seq_cst 栅栏.
为什么你认为这个实现不起作用?

本文地址:https://www.itbaoku.cn/post/1050768.html