在C#中进行多线程处理时,为什么要使bool不稳定? 我理解C++中的代码>但在C#中,它似乎有不同的含义,与多线程更相关。我认为bool操作是原子的,我认为如果操作是原子的,就不会有线程问题。我错过了什么
我以为布尔运算是原子的 它们确实是原子的 我认为如果操作是原子的,就不会有线程问题 这就是你有一个不完整的图片。假设有两个线程运行在不同的内核上,每个内核都有自己的缓存层。线程1的缓存中有在C#中进行多线程处理时,为什么要使bool不稳定? 我理解C++中的代码>但在C#中,它似乎有不同的含义,与多线程更相关。我认为bool操作是原子的,我认为如果操作是原子的,就不会有线程问题。我错过了什么,c#,multithreading,C#,Multithreading,我以为布尔运算是原子的 它们确实是原子的 我认为如果操作是原子的,就不会有线程问题 这就是你有一个不完整的图片。假设有两个线程运行在不同的内核上,每个内核都有自己的缓存层。线程1的缓存中有foo,线程2更新foo的值。线程1不会看到更改,除非foo标记为volatile,获取锁,使用类,或显式调用Thread.MemoryBarrier(),这将导致值在缓存中无效。因此: 使用volatile修饰符可以确保一个线程检索另一个线程写入的最新值 Eric Lippert有一篇关于波动性的文章,他解释
foo
,线程2更新foo
的值。线程1不会看到更改,除非foo
标记为volatile
,获取锁
,使用类,或显式调用Thread.MemoryBarrier()
,这将导致值在缓存中无效。因此:
使用volatile修饰符可以确保一个线程检索另一个线程写入的最新值
Eric Lippert有一篇关于波动性的文章,他解释道:
volatile读取的真正语义
写作比我在这里概述的要复杂得多;在里面
事实上,它们并不能保证每个处理器都停止它所做的事情
正在执行和更新主存的缓存。相反,它们提供
关于如何在读和写之前和之后访问内存的较弱保证
可以观察到写操作彼此之间的顺序。
编辑:
根据@xanatos的评论,这并不意味着
volatile
保证立即读取,它保证读取最新的值。C#中的volatile
关键字都是关于读/写重新排序的,所以这是一个相当深奥的东西
(我认为是线程的“圣经”之一)写道:
volatile关键字指示编译器在每次读取该字段时生成一个获取围栏,在每次写入该字段时生成一个释放围栏。获取围栏防止在围栏之前移动其他读/写操作;释放围栏可防止其他读/写操作在围栏之后移动。这些“半围栏”比全围栏快,因为它们为运行时和硬件提供了更多的优化空间 这是很难理解的:——) 现在。。。这并不意味着什么:- 这并不意味着现在将读取值/现在将写入值
object myrefthatissharedwithotherthreads = new MyClass(5);
在哪里
现在。。。这个表达可以想象为:
var temp = new MyClass();
temp.Value = 5;
myrefthatissharedwithotherthreads = temp;
其中temp
是由编译器生成的、您看不到的内容
如果写入操作可以重新排序,则可以:
var temp = new MyClass();
myrefthatissharedwithotherthreads = temp;
temp.Value = 5;
另一个线程可能会看到部分初始化的
MyClass
,因为在类MyClass
完成初始化之前,与其他线程共享的myref的值是可读的。volatile
防止围绕变量访问的指令重新排序。这相当复杂。实际上,在C++和C语言之间的语义的语义差异方面有很好的描述。volatile
write不能保证立即写入。@xanatos我不是说它保证立即写入。声明它保证了对更新值的读取。您在写入volatile
:-)两次写入,两次出错:-):-)@xanatos a crap:X时遇到问题。修复了此问题。volatile
不仅阻止了围栏,还阻止了诸如持续传播、死代码消除或寄存器提升等优化。它比简单的栅栏更具限制性。@Cicada你有没有关于C#的参考资料?
var temp = new MyClass();
myrefthatissharedwithotherthreads = temp;
temp.Value = 5;