使用Interlocked.Exchange更新引用和Int32

Ole*_*nyk 17 .net c# multithreading

众所周知,引用在32位处理器中占用4字节,在64位处理器中占用8字节.因此,处理器保证以机器的自然字大小为增量单次读取和写入存储器将以原子方式执行.另一方面,Interlocked类有两种方法:

public static int Exchange(
    ref int location1,
    int value
)
Run Code Online (Sandbox Code Playgroud)

public static T Exchange<T>(
    ref T location1,
    T value
)
where T : class
Run Code Online (Sandbox Code Playgroud)

那么,问题是为什么Int32和参考类型需要Interlocked.Exchange?仅使用简单的赋值就不能安全地完成,因为它是原子的吗?

Dmi*_*try 14

它不仅仅是关于原子性的.它还涉及内存可见性.变量可以存储在主存储器或CPU缓存中.如果变量仅存储在CPU缓存中,则在不同CPU上运行的线程将无法看到该变量.考虑以下示例:

public class Test {
    private Int32 i = 5;

    public void ChangeUsingAssignment() {
        i = 10;
    }

    public void ChangeUsingInterlocked() {
        Interlocked.Exchange(ref i, 10);
    }

    public Int32 Read() {
        return Interlocked.CompareExchange(ref i, 0, 0);
    }
}
Run Code Online (Sandbox Code Playgroud)

现在,如果你在一个线程上调用'ChangeUsingAssignment'而在另一个线程上调用'read',则返回值可能是5,而不是10.但是如果你调用ChangeUsingInterlocked,'Read'将按预期返回10.

 ----------         ------------         -------------------
|   CPU 1  |  -->  |   CACHE 1  |  -->  |                   |
 ----------         ------------        |                   |
                                        |        RAM        |
 ----------         ------------        |                   |
|   CPU 2  |  -->  |   CACHE 2  |  -->  |                   |
 ----------         ------------         -------------------
Run Code Online (Sandbox Code Playgroud)

在上图中,"ChangeUsingAssignement"方法可能导致值10在CACHE 2中"卡住"并且不会使其进入RAM.当CPU 1稍后尝试读取它时,它将从RAM中获取值仍然是5.使用Interlocked而不是普通写入将确保值10一直到RAM.

  • 我知道这显然是一年之后,但如果可能的话,请你回顾一下吗?该网站http://igoro.com/archive/volatile-keyword-in-c-memory-model-explained/似乎暗示C#中的所有写入都已经不稳定. (3认同)
  • 您对内存可见性是正确的,但您的解释不正确。一旦该值进入处理器缓存,它就对所有物理 CPU 内核可见。这是由 https://en.wikipedia.org/wiki/Cache_coherence 保证的。内存可见性的问题是当值仍在 CPU 内核(不是 CPU L1、L2、L3 缓存)内的读/写缓冲区中时 (3认同)

Dam*_*ver 9

Interlocked.Exchange有一个返回值,让您知道您刚刚更换的值.它是设置新值获取这些方法实现的旧值的组合.


InB*_*een 6

Interlock.Exchange执行原子操作时返回原始值.重点是提供一种锁定机制.所以它实际上是两个操作:读取原始值设置新值.这两者并不是原子的.


Mar*_*age 5

交换内存值和CPU寄存器的内容通常不是原子的.你们都需要读写内存位置.此外,这些Interlocked方法保证即使在多核计算机上操作也是原子的,其中每个核心都有自己的高速缓存,并且可能有自己的主存储器视图.