我非常了解C ++中的volatile
关键字。但在C#中,它似乎采用了不同的含义,与多线程更相关。我认为bool操作是原子的,我认为如果操作是原子的,你就不会有线程问题。我错过了什么?
答案 0 :(得分:4)
我认为bool操作是原子的
他们确实是原子的。
我认为如果操作是原子的,你就不会有线程问题。
这是你的照片不完整的地方。想象一下,您有两个线程在不同的核心上运行,每个核心都有自己的缓存层。线程#1在其缓存中有foo
,而线程#2更新foo
的值。线程#1不会看到更改,除非foo
标记为volatile
,获得lock
,使用Interlocked
类或明确调用{{1}这会导致值在缓存中失效。因此,guaranteeing that you read the most up to date value:
使用volatile修饰符可确保一个线程检索另一个线程写入的最新值。
Eric Lippert有一个关于波动的great post,他解释说:
volatile读取的真正语义 写作比我在这里概述的要复杂得多;在 事实上,他们实际上并不能保证每个处理器都能阻止它 正在进行和更新主存储器的缓存。相反,它们提供 关于读取和读取之前和之后内存访问的方式的较弱保证 可以观察到相对于彼此的订单。
修改强>
根据@xanatos评论,并不意味着 Thread.MemoryBarrier()
保证立即读取,它保证读取最新的值。
答案 1 :(得分:3)
C#中的volatile
关键字是关于读/写重新排序的,所以它是非常深奥的。
http://www.albahari.com/threading/part4.aspx#_Memory_Barriers_and_Volatility
(我认为是#34;圣经"关于线程之一)写道:
volatile关键字指示编译器在每次从该字段读取时生成获取栅栏,并在每次写入该字段时生成释放栅栏。获取栅栏可防止其他读/写在栅栏前移动;释放栅栏可防止在栅栏后移动其他读/写。这些“半栅栏”比完全栅栏更快,因为它们为运行时和硬件提供了更多的优化空间。
这是一件非常难以理解的事情: - )
现在......它的意思并非:
它只是意味着如果你从一个易变的变量中读取一些东西,那么在此之前已经读过/写过的所有其他东西"特殊的"读完后不会被移动"特别"读。所以它创造了一个障碍。矛盾的是,通过读取一个易变量变量,您可以保证在读取时对所有其他变量(易失或不变)所做的所有写操作都将完成。
易失性写入可能更重要,并且部分由英特尔CPU保证,而第一版Java无法保证:没有写入重新排序。问题是:
object myrefthatissharedwithotherthreads = new MyClass(5);
,其中
class MyClass
{
public int Value;
MyClass(int value)
{
Value = value;
}
}
现在......表达可以想象成:
var temp = new MyClass();
temp.Value = 5;
myrefthatissharedwithotherthreads = temp;
其中temp
是编译器生成的,您无法看到的内容。
如果可以重新排序写入,则可以:
var temp = new MyClass();
myrefthatissharedwithotherthreads = temp;
temp.Value = 5;
,另一个线程可以看到部分初始化的MyClass
,因为myrefthatissharedwithotherthreads
的值在类MyClass
完成初始化之前是可读的。