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