基本的原始读写操作是否需要 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
方法中,您可以确保每次调用该方法时都会读取该条。
我读过很多比较 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
方法中,您可以确保每次调用该方法时都会读取该条。