29

这是一个复杂的问题,请在回答之前仔细考虑。

考虑这种情况。两个线程(一个读取器和一个写入器)访问一个全局int. 这安全吗?通常,我会不假思索地回应,是的!

然而,在我看来,赫伯萨特并不这么认为。在他关于有效并发的文章中,他讨论了一个有缺陷的无锁队列更正的版本

在第一篇文章的结尾和第二篇文章的开头,他讨论了一个很少被考虑的变量特征,即写顺序。整数是原子的,很好,但整数不一定是有序的,这可能会破坏任何无锁算法,包括我上面的场景。我完全同意,在当前和未来的所有平台上保证正确的多线程行为的唯一方法是使用原子(AKA 内存屏障)或互斥锁。

我的问题; 在真正的硬件上写重排是一个问题吗?还是多线程的偏执狂只是迂腐?
那么经典的单处理器系统呢?
更简单的 RISC 处理器(如嵌入式 power-pc)呢?

澄清:我对 Sutter 先生所说的关于硬件(处理器/缓存)重新排序变量写入的内容更感兴趣。我可以通过编译器开关或在编译后手动检查程序集来阻止优化器破坏代码。但是,我想知道硬件在实践中是否仍然会弄乱代码。

4

9 回答 9

26

您检查组件的想法不够好;重新排序可以发生在硬件级别。

要回答您的问题“这是否是读取硬件的问题:”是的! 事实上,我自己也遇到过这个问题。

可以绕过单处理器系统或其他特殊情况的问题吗?我会争辩说“不”,因为五年后你可能需要在多核上运行,然后找到所有这些位置会很棘手(不可能?)。

一个例外:专为嵌入式硬件应用程序设计的软件,您确实可以完全控制硬件。事实上,在 ARM 处理器等情况下,我已经像这样“作弊”了。

于 2009-01-03T19:59:07.433 回答
9

是的 - 在需要的地方使用内存屏障来防止指令重新排序。在一些 C++ 编译器中,volatile 关键字已扩展为每次读取和写入插入隐式内存屏障——但这不是一个可移植的解决方案。(对于 Interlocked* win32 API 也是如此)。Vista 甚至添加了一些新的更细粒度的 Interlocked API,让您可以指定读取或写入语义。

不幸的是,C++ 的内存模型如此松散,以至于任何类型的代码在某种程度上都是不可移植的,您必须为不同的平台编写不同的版本。

于 2009-01-03T19:56:55.470 回答
5

就像您说的那样,由于在缓存或处理器级别进行了重新排序,您实际上确实需要某种内存屏障来确保正确同步,尤其是对于多处理器(尤其是在非 x86 平台上)。(我相信单处理器系统不存在这些问题,但请不要引用我的话——我当然更倾向于安全行事,无论如何都要进行同步访问。)

于 2009-01-03T19:51:13.547 回答
5

我们遇到了这个问题,尽管在指令重新排序比 x86/x64 更激进的安腾处理器上。

解决方法是使用 Interlocked 指令,因为(当时)没有办法告诉编译器只是在赋值后设置一个写屏障。

我们真的需要语言扩展来干净地处理这个问题。在您试图从一段代码中挤出尽可能多的性能的情况下,使用 volatile(如果编译器支持)过于粗略。

于 2009-01-03T20:16:50.627 回答
4

这是真实硬件上的问题吗?

绝对可以,尤其是现在,随着当前和未来 CPU 转向多核。如果您依赖有序原子性来实现应用程序中的功能,并且您无法通过您选择的平台或使用同步原语来保证这一要求,那么在所有情况下,即客户从单核 CPU 移动到多核 CPU ,那么您只是在等待问题发生。

引用 Herb Sutter 的文章(第二篇)

有序原子变量在流行的平台和环境中以不同的方式拼写。例如:

  • volatile在 C#/.NET 中,如volatile int.
  • volatile或 * Atomic* 在 Java 中,如volatile int, AtomicInteger.
  • atomic<T>在即将发布的 ISO C++ 标准 C++0x 中,如atomic<int>.

我还没有看到 C++0x 如何实现有序原子性,所以我无法指定即将推出的语言功能是纯库实现还是依赖于对语言的更改。在新标准可用之前,您可以查看该提案以查看它是否可以作为非标准扩展合并到您当前的工具链中,甚至可能已经适用于您的情况。

于 2009-01-03T20:16:37.397 回答
3

这是真实硬件上的问题。我的一个朋友在 IBM 工作,主要靠找出客户代码中的这类问题为生。

如果您想了解情况会变得多么糟糕,请搜索有关 Java 内存模型(以及现在的 C++ 内存模型)的学术论文。考虑到真正的硬件可以做的重新排序,试图找出高级语言中的安全是一场噩梦。

于 2009-01-03T20:08:40.360 回答
2

No this isn't safe and there is real hardware avaialble that exhibits this problem, for example the memory model in the powerpc chip on xbox 360 allows writes to be reordered. This is exacerbated by the lack of barriers in the intrinsics, see this article on msdn for more details.

于 2010-10-17T05:03:05.363 回答
1

“它是否安全”这个问题的答案本质上是模棱两可的。

从某种意义上说,您的计算机不会着火,即使是双打也始终是安全的。它是安全的,因为你总是会得到一个 int 在过去某个时间持有的值,它是不安全的,因为你可能会得到一个被/将被另一个线程更新的值。

“原子”意味着您获得了第二个保证。由于 double 通常不是原子的,因此您可以获得 32 个旧位和 32 个新位。这显然是不安全的。

于 2009-01-05T09:55:04.453 回答
1

当我问到我对单处理器powerpc 最感兴趣的问题时。InSciTek Jeff在其中一条评论中提到了 powerpc SYNC 和 ISYNC 指令。确定答案的关键所在。我在 IBM 的网站上找到了

这篇文章很大而且很密集,但带走的是不,它不安全。在较旧的 powerpc 上,内存优化器不够复杂,不会导致单处理器出现问题。然而,较新的更具侵略性,甚至可以破坏对全局 int 的简单访问。

于 2009-01-05T16:09:36.660 回答