Home > database >  Is volatile is needed for basic primitive read write operations?
Is volatile is needed for basic primitive read write operations?

Time:03-25

I've read many articles that compare volatiles and locks. From my understanding lock guarantees that only one thread can use the lock and run the critical code in parallel, and volatile disables the memory optimization - so they are not replaceable. Using volatile should not replace a lock. Although most of the articles are not using lock when they show examples of volatile.

Say I have such a super simple 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;
        }
    }
}

Say the ToggleBar is used by thread A and ReadBar by thread B, only.

The lock protects from the two threads to access the variable in parallel. But should bar be volatile as well?

In most of the articles I've read, this was the "recommended" solution (also Interlocked class), but for my understanding it, it doesn't solve the issue that volatile solves - even with the lock thread B may read an old value of bar due to memory optimizations and caching, isn't it?

Shouldn't bar be a volatile in addition to the lock in this case?

CodePudding user response:

The runtime is allowed to move memory access for optimization. If there is a lock, the memory access may not be moved before entering or after leaving the lock. See also this 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.

Hence for example in your ToggleBar method, you can be sure that the bar variable is read after the method is entered and it has been written when the method is left. In your ReadBar method, you can be sure that the bar is read every time when the method is called.

  • Related