.net 4.0中引用类型的Volatile
本文关键字:Volatile 引用类型 net | 更新日期: 2023-09-27 18:18:54
我对volatile
的引用类型感到困惑。
我知道对于原始类型,volatile
可以立即反映来自另一个线程的值变化。对于引用类型,它可以立即反映地址的变化。但是,对象的内容呢?它们还在缓存中吗?
(假设List.Add()
是原子操作)
例如:
class A
{
volatile List<String> list;
void AddValue()
{
list.Add("a value");
}
}
如果一个线程调用AddValue
函数,列表的地址没有变化,另一个线程是否会更新列表的"内容"变化,或者内容可能被每个线程缓存而不为其他线程更新?
我明白,对于基本类型,volatile可以立即反映来自另一个线程的值变化
你至少在三个方面理解错误。在深入理解关于弱内存模型、获取和释放语义以及它们如何影响程序之前,不应该尝试使用volatile。
首先,要清楚volatile影响的是变量,而不是值。第二,volatile对包含值类型的值的变量的影响和对包含引用的变量的影响是一样的。
第三,volatile并不意味着其他线程的值更改立即可见。Volatile意味着变量具有获取和释放语义。Volatile影响的顺序,可以从特定线程观察到内存突变的副作用。存在一致的普遍突变顺序,并且可以从所有线程立即观察到这种顺序的突变,这种想法并不是内存模型所做的保证。
但是,对象的内容呢?
怎么了?引用类型的易失性变量所引用的存储位置不需要具有任何特定的线程特征。
如果一个线程调用AddValue函数,列表的地址没有改变,另一个线程会得到关于列表"内容"变化的更新吗?
不。为什么呢?其他线程可能在不同的处理器上,并且处理器缓存可能已经预加载了包含支持列表的数组地址的页面。修改列表可能会改变包含数组地址的存储位置,使其指向某个完全不同的位置。
当然,list类从一开始就不是线程安全的。如果你没有锁定对列表的访问权限,那么当你尝试这样做时,列表可能会崩溃并死亡。你不需要volatile;您需要的是将线程锁放在对列表的访问周围。由于线程锁诱导了全围栏,所以不需要volatile引入的半围栏。
比那还糟。
如果你并发访问一个非线程安全的对象,你的程序可能会崩溃。得到过时的信息并不是最坏的潜在结果。
当在线程之间共享。net基类库对象时,除了使用锁别无选择。对于无锁编程,您需要在最低级别对数据结构进行侵入性更改。
volatile
关键字对列表的内容(或者更准确地说,对被引用的对象)没有影响。
lock
语句来同步对共享列表的访问。否则,您将面临可能导致程序崩溃的竞争条件。List<T>
类本身不是线程安全的。
查看http://www.albahari.com/threading/part4.aspx#_The_volatile_keyword可以很好地解释volatile的实际作用以及它如何影响字段。
该网站的整个线程部分是必须阅读的,它包含了大量有用的信息,这些信息在我设计多线程软件时被证明是非常有用的。