3

我有一些简单的(希望是)问题,我一直无法找到答案 -

假设我有多个线程可以访问的对象 a、b。

Interlocked.Exchange(ref a, b)

如果 'b' 不是 volatile,这个操作会这样对待它吗?即它会从内存中获取这个变量的最新值吗?如果是这样,那是写的“原子”读吗?我知道 Interlocked.Exchange 的主要目的是您将获得以前的“a”值作为新写入的原子操作。但我的主要困惑是“b”的实际值被写入“a”。

我的第二个问题与本文中的一句话有关:

http://igoro.com/archive/volatile-keyword-in-c-memory-model-explained/

“一个有趣的点是,根据此处和此处记录的内存模型,C# 中的所有写入都是易失性的,并且可能也是这样实现的。C# 语言的 ECMA 规范实际上定义了一个较弱的模型,其中默认情况下写入不是易失性的。”

这是真的?如果是这样,如果不关心“a”的先前值,是否有 Interlocked.Exchange 的目的?(关于我的第一个例子)。我没有看到关于 StackOverflow 的任何其他文章或评论,因为每次写入都是易失性的。但是,我知道写入是原子的。

编辑:如果我的第一个问题的答案是“b”不被视为易失性,而我的第二个问题的答案是写入确实是易失性的,那么跟进是,如果我们不这样做,什么时候 interlocked.exhange 有用'不在乎'a'的先前值?

4

3 回答 3

7

传递给的变量Exchange(或传递给任何方法的任何 volatile 变量)在传递时不会保留“波动性”......实际上不需要它volatile(在方法调用期间),因为唯一要做的volatile就是确保编译器不优化变量的使用(这通常意味着优化写入寄存器,因此该值只能被单个处理器“看到”)。在 x86/x64 以外的处理器上,这有时意味着保证获取或释放语义的指令。.NET 不使用寄存器来传递参数,因此 volatile 不会影响传递的参数的“波动性”。由于内存模型的可见性保证,它必须始终从内存中获取最新值

RE问题2:报价是“有点”真实的,取决于字段的声明,字段有可见性保证;但如果没有“易失性”字段访问,则可以在某些使用阶段将其优化为寄存器,从而可能隐藏其他处理器的某些写入。

Interlocked交换使非原子的操作看起来是原子的。交换性质类似于:

var x = someVariable;
someVariable = y;

无论someVariable. Exchange使这个操作原子化。这也是原子的,具有非原子类型,如double, long(32 位)等。

使这个原子化的部分原因Exchange是使用内存栅栏——它使写入可见,并且在内存栅栏之后的指令序列中读取相同内存地址时不会重新排序。

Exchange如果您不关心“a”的先前值,为什么要使用?如果您不关心实际的“交换”,那么VolatileWrite似乎更合适。

或者,如果不需要“交换”,您可以编写线程安全代码来模拟“A = B”,如下所示:

Thread.MemoryBarrier();
A=B;

FWIWInterlocked部分围绕某些处理器中的比较和交换 (CAS) 指令建模。这些指令允许您在一条指令中执行这两个操作(使其成为原子)。如果没有类似的东西Interlocked,编译器可能很难推断出应该使用这些 CAS 指令之一。此外,Interlocked在不支持这些 CAS 指令的处理器上提供原子使用(以及其他潜在的非原子指令,如 inc 和 dec——可能并非在所有处理器上都可用)

于 2012-10-22T14:46:56.857 回答
1

如果 'b' 不是 volatile,这个操作会这样对待它吗?

我认为您不应该在b共享变量时使用它。这样就消除了整个问题。但 Exchange 将始终使用 Memorybarrier,因此答案可能是肯定的。

如果我们不关心“a”的先前值,interlocked.exhange 什么时候有用?

for 的重载double非常有用,因为写入操作double不是原子的。32 位系统上的 Int64 相同。

但是对于Exchange()原子类型的重载,用例不太清楚。我认为大多数算法都会支持CompareExcange().

所以认为它是原子的 Write()。

于 2012-10-22T14:36:07.990 回答
1

如果 'b' 不是 volatile,这个操作会这样对待它吗?

是的,因为根据这个来源Interlocked类的所有方法都会生成隐式内存栅栏。

于 2012-10-22T14:37:58.187 回答