在仅包含该语句的方法中用简单赋值替换 Volatile.Write() 是否安全?

Is it safe to replace Volatile.Write() with simple assignment in a method that contains only that statement?

这是一道 C# 多线程高级题。

假设我有这段代码用作锁定机制,只允许一个线程启动某些操作:

private static int _guard = 0;

private static bool acquire() {
    return Interlocked.CompareExchange(ref _guard, 1, 0) == 0;
}

private static void release() {
    Volatile.Write(ref _guard, 0);
}

此锁用于保护当时只能由一个线程执行的方法:

public readonly Status Status = new (); // updated from thread that runs someTask

void TryRunningTask {
    if (acquire()) {
        return await someTask();
    } else {
        InfoMessage = "Another user is currently running someTask.";
    }
}

我的问题是,如果我将 release() 更改如下:

private static void release() {
    _guard = 0;
}

程序的行为是否仍然完全相同?那会破坏线程安全吗?这种变化有意义吗?


我提出此更改的原因如下:

  1. release() 方法中没有其他 read/write 操作。 在 Volatile.Write method 的 MS 文档中,它说:

Writes a value to a field. On systems that require it, inserts a memory barrier that prevents the processor from reordering memory operations as follows: If a read or write appears before this method in the code, the processor cannot move it after this method.

所以因为我的 release() 方法没有其他操作 before/after Volatile.Write() 调用我想我可以用简单的赋值语句替换它 _guard = 0; 对吗?

  1. 根据 C# Standard 第 10.6 节,_guard = 0; 操作保证是原子操作:

10.6 Atomicity of variable references

Reads and writes of the following data types shall be atomic: bool, char, byte, sbyte, short, ushort, uint, int, float, and reference types.

不,您不能删除对 Volatile.Write 的调用。

关于原子性点,您是正确的:C# 和 CLR 要求 32 位和更小的数据类型应该是原子的。

然而,要考虑的不仅仅是原子性。还需要考虑指令重新排序和处理器缓存。

重新排序可能由 CLR 抖动引起,并且您的函数的大小无关紧要,因为它可能被内联到调用它的任何函数中(并且可能会被认为是短的)。

处理器也可以重新排序指令,如果它符合给定的指令。

所以这个需要一个内存屏障thread-safe。


处理器缓存是另一个问题:如果处理器内核没有被告知读取或写入是易变的,它可能只使用自己的缓存而忽略其他内核缓存中的内容。

但是,Volatile.Write 可能还不够。我无法从您所展示的内容中准确判断,但您似乎有多个线程读取 写入。因此,我认为您应该改用 Interlocked.Exchange