基本的原始读写操作是否需要 volatile?

Is volatile is needed for basic primitive read write operations?

我读过很多比较 volatiles 和锁的文章。根据我的理解,lock 保证只有一个线程可以并行使用锁和 运行 关键代码,而 volatile 禁用内存优化 - 因此它们不可替换。使用 volatile 不应替换 lock。尽管大多数文章在显示 volatile.

的示例时并未使用 lock

说我有这么个超简单的class:

class Foo 
{
    private bool bar;
    private static readonly object barLock = new object();

    public void ToggleBar()
    {
        lock (barLock)
        {
            bar = !bar;
        }
    }

    public bool ReadBar()
    {
        lock (barLock)
        {
            return bar;
        }
    }
}

假设 ToggleBar 仅供线程 A 使用,ReadBar 仅供线程 B 使用。

lock防止两个线程并行访问变量。但是 bar 也应该是 volatile 吗?

在我读过的大多数文章中,这是“推荐”的解决方案(也是 Interlocked class),但据我了解,它并没有解决 volatile 解决了 - 即使有锁,线程 B 也可能由于内存优化和缓存而读取 bar 的旧值,不是吗?

在这种情况下 bar 不应该是 volatile 除了 之外的 lock 吗?

允许运行时移动内存访问以进行优化。如果有锁,在进入锁之前或离开锁之后,内存访问可能不会移动。另见 blog post by Eric Lippert:

The C# compiler, JIT compiler and CPU are all permitted to make optimizations provided that the optimization is undetectable by the current thread. In particular, reads and writes of variables may be re-ordered or eliminated entirely provided that doing so is not observable to the current thread. [...] A lock introduces a restriction on these optimizations. In particular: no read may be moved before the beginning of a lock statement, and no write may be moved after the end of a lock statement.

因此,例如在您的 ToggleBar 方法中,您可以确定 bar 变量在方法进入后被读取,并且在方法离开时已被写入。在您的 ReadBar 方法中,您可以确保每次调用该方法时都会读取该条。