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.