我对volatile
的参考类型感到困惑。
我理解,对于原始类型,volatile
可以立即反映来自另一个线程的值更改。对于引用类型,它可以立即反映地址更改。但是,对象的内容呢。他们还在缓存吗?
(假设List.Add()
是原子操作)
例如,我有:
class A
{
volatile List<String> list;
void AddValue()
{
list.Add("a value");
}
}
如果一个线程调用函数AddValue
,则列表的地址不会更改,另一个线程是否会更新列表的“内容”更改,或者内容可能会被缓存为每个线程并且它不会其他线程更新?
答案 0 :(得分:20)
据我所知,对于原始类型,volatile可以立即反映另一个线程的值变化
至少在三种方面你理解不正确。在深入理解关于弱内存模型,获取和释放语义以及它们如何影响程序的所有之前,不应尝试使用volatile。
首先,要明确volatile会影响变量,而不会影响值。
其次,volatile不会影响包含值类型值的变量,只会影响包含引用的变量。
第三, volatile并不意味着可以立即看到来自其他线程的值更改。易失性意味着变量具有获取和释放语义。挥发性影响从特定线程可以观察到内存突变的副作用的顺序。存在一致的通用突变顺序并且可以从所有线程即时观察到那种突变的想法并不是内存模型的保证。
但是,对象的内容呢?
怎么样?由参考类型的易变变量引用的存储位置不需要具有任何特定的线程特性。
如果一个线程调用函数AddValue,列表的地址不会改变,另一个线程会更新列表的“内容”更改。
不。为什么会这样?该另一个线程可能位于不同的处理器上,并且该处理器缓存可能已预先加载了包含支持该列表的阵列的地址的页面。改变列表可能已经改变了包含数组地址的存储位置,以引用一些完全不同的位置。
当然,列表类首先不是线程安全的。如果您没有锁定对列表的访问权限,那么当您尝试执行此操作时,列表可能会崩溃并死亡。
你不需要挥发性;你需要的是在访问列表周围放置线程锁。由于线程锁定会导致完全围栏,因此不需要使用volatile引入的半围栏。
答案 1 :(得分:6)
比这更糟糕。
如果您同时访问非线程安全的对象,您的程序可能会崩溃。获取过时的信息并不是最糟糕的结果。
在线程之间共享.NET基类库对象时,除了使用锁定之外别无选择。对于无锁编程,您需要在最低级别对数据结构进行侵入式更改。
答案 2 :(得分:2)
volatile
关键字对列表的内容没有影响(或者更确切地说,是被引用的对象)。
对于另一个线程来说更新 / 未更新是对过去发生的事情的过度简化。您应该使用lock
语句来同步对共享列表的访问。否则,您实际上面临可能导致程序崩溃的竞争条件。 List<T>
类本身不是线程安全的。
答案 3 :(得分:2)
请查看http://www.albahari.com/threading/part4.aspx#_The_volatile_keyword,了解有关挥发性实际上做什么以及它如何影响字段的详细解释。
无论如何,该网站上的线程的整个部分是必读的,它包含大量有用的信息,这些信息在我设计多线程软件时对我来说非常有用。