20

Win32 api 有一组 InterlockedXXX 函数来原子地和同步地操作简单的变量,但是似乎没有任何 InterlockedRead 函数来简单地检索变量的值。怎么来的?

MSDN 说

对正确对齐的 32 位变量的简单读写是原子操作

但补充说:

但是,不能保证访问是同步的。如果两个线程正在读取和写入同一个变量,则无法确定一个线程是否会在另一个线程执行其写入操作之前执行其读取操作。

这意味着,据我了解,一个变量的简单读取操作可以发生,而另一个,比如说,InterlockedAdd 操作已经到位。那么为什么没有联锁函数来读取变量呢?

我猜这个值可以被读取为 InterlockedAdd-ing 零的结果,但这似乎不是正确的方法。

4

3 回答 3

8

实现这一点的正常方法是使用InterlockedCompareExchange64两个值相同的比较交换操作(例如)。由于某种原因,我偷偷怀疑这可以比添加 0 更有效,但我没有证据支持这一点。

有趣的是,.NET 的类直到 .NET 2.0 才Interlocked获得方法。Read我相信这Interlocked.Read是使用Interlocked.CompareExchange. (请注意,文档让Interlocked.Read我感到有些误导——它谈论的是原子性,而不是易变性,这在 .NET 上意味着一些非常具体的东西。我不确定 Win32 内存模型如何保证来自不同的新写入值的可见性线程,如果有的话。)

于 2009-07-05T18:43:30.000 回答
7

我认为您对“不同步”的解释是错误的。简单读取原子的,但您必须自己处理重新排序和内存可见性问题。前者是通过在适当的地方使用栅栏指令来处理的,后者与读取无关(但潜在的并发写入必须确保适当的可见性,如果互锁函数映射到 LOCKED asm 指令则应该这样做)。

于 2009-07-05T20:10:42.257 回答
2

整个讨论的关键是正确的对齐,这是在 xxx 的 Partition I 中的“”部分中定义12.6.2 Alignment的:

Built-in datatypes shall be properly aligned, which is defined as follows:
• 1-byte, 2-byte, and 4-byte data is properly aligned when it is stored at
  a 1-byte, 2-byte, or 4-byte boundary, respectively.
• 8-byte data is properly aligned when it is stored on the same boundary
  required by the underlying hardware for atomic access to a native int.

基本上,所有 32 位值都具有所需的对齐方式,而在 64 位平台上,64 位值也具有所需的对齐方式。

但请注意:有一些属性可以显式更改内存中类的布局,这可能会导致您失去这种对齐方式。这些是专门用于此目的的属性,因此除非您已着手更改布局,否则这不适用于您。

除此之外,该类的目的Interlocked是提供(解释)只能在“之前”或“之后”状态下观察到的操作。联锁操作通常只在修改内存时才需要考虑(通常以一些非平凡的比较交换类型的方式)。正如您找到的 MSDN 文章所指出的,读取操作(正确对齐时)在任何时候都可以被视为原子操作,无需进一步的预防措施。

然而,在处理读取操作时还有其他注意事项:

  • 在现代 CPU 上,虽然读取可能是原子的,但它也可能从某处过时的缓存中返回错误的值......这是您可能需要使字段“易失”以获得您期望的行为的地方
  • 如果您在 32 位硬件上处理 64 位值,您可能需要使用该Interlocked.Read操作来保证在单个原子操作中读取整个64 位值(否则可能会作为 2 个单独的 32 位可以从内存更新的任一侧读取)
  • 重新排序读取/写入可能会导致您无法获得预期的值;在这种情况下,可能需要一些内存屏障(显式或通过使用Interlocked类操作)

简短摘要;就原子性而言,您正在做的事情很可能不需要任何特殊的阅读指令……但是,您可能需要注意其他事情,具体取决于您在做什么。

于 2009-07-06T01:57:44.870 回答