当前位置: 首页 > 编程语言 > C#

Thread.VolatileRead实现分享

时间:2023-04-10 20:24:55 C#

Thread.VolatileRead实现正在研究VolatileRead/VolatileWrite方法的实现(使用Reflector),一头雾水。下面是VolatileRead的实现:[MethodImpl(MethodImplOptions.NoInlining)]publicstaticintVolatileRead(refintaddress){intnum=address;内存屏障();返回数;为什么在读取“地址”的值后放置内存屏障?不应该反过来吗?(放置在读取值之前,因此任何对“地址”的未决写入都将在我们实际读取时完成。VolatileWrite也会发生同样的事情,内存屏障放置在赋值之前。为什么?另外,为什么这些方法有NoInlining属性?如果被内联会怎样?我最近想了想这就是规范所保证的——对于volatile写入,它保证在volatile之后不会移动到之前的写入。你不是唯一怀疑这段代码的人,但JoeDuffy解释得比我好?我对此的回答是放弃无锁编码,而不是使用像PFX这样旨在让我远离的东西。记住模型对我来说太难了——我会把它留给专家,坚持我认为安全的东西。一个那天,我会更新我的线程文章来反映这一点,但我认为我需要能够更智能地讨论它......(我不知道非内联部分,顺便说一句。我怀疑内联可能会引入一些其他的优化不会发生在volatile读/写操作上,但我很容易出错...)也许我过于简单化了,但我考虑了重新排序和缓存一致性等的解释给出了太多的细节。那么,为什么MemoryBarrier是在实际读取之后呢?我将尝试通过一个使用对象而不是int的示例来解释这一点。有人可能认为这是正确的:线程1创建对象(初始化其内部数据)。线程1然后将该对象放入一个变量中。然后它“做围栏”并且所有线程都看到新值。然后,读取是这样的:线程2“做围栏”。线程2读取对象实例。线程2确保它拥有该实例的所有内部数据(因为它是从栅栏开始的)。最大的问题是:线程1创建对象并初始化它。线程1然后将该对象放入一个变量中。在线程刷新缓存之前,CPU本身会刷新部分缓存……它只提交变量的地址(而不是该变量的内容)。在那一刻,线程2已经刷新了它的缓存。所以它将从主内存中读取所有内容。因此,它读取变量(它在那里)。然后它读取内容(不存在)。最后,在所有这些之后,CPU1执行执行栅栏的线程1。那么,易失性写入和读取会发生什么情况?volatilewrite立即将对象的内容写入内存(由fence启动),然后设置变量(可能不会立即进入实际内存)。易失性读取将首先清除缓存。然后它读取该字段。如果它在读取字段时收到一个值,则可以确定引用指向的内容确实存在。有了这些小东西,是的,您可以执行VolatileWrite(1)并且另一个线程仍然会看到零值。但是,一旦其他线程看到值1(使用易失性读取),则可能引用的所有其他项都已经存在。你不能真正告诉它,当读取旧值(0或null)时,考虑到你还没有你需要的一切,你很容易就停止了。我已经看到一些讨论,即使缓存被刷新两次,正确的模式也是:MemoryBarrier-将刷新在此调用之前更改的其他变量WriteMemoryBarrier-将保证写入被刷新读取将需要相同的:MemoryBarrierRead-保证我们看到最新的信息……也许是我们记忆块之后的信息。由于某些内容可能已经存在于我们的MemoryBarrier之后并且已经被读取,因此我们必须使用另一个MemoryBarrier来访问这些内容。如果.Net中存在的话,它们可能是两个写栅栏或两个读栅栏。我说的都不是很确定……这是我得到的很多信息的“汇编”,它确实解释了为什么VolatileRead和VolatileWrite看起来是相反的,但它也保证在使用它们时不会读取无效值。以上就是C#学习教程的全部内容:Thread.VolatileRead实现分享。如果对你有用,需要进一步了解C#学习教程,希望大家多多关注。本文收集自网络,不代表立场。如涉及侵权,请点击右侧联系管理员删除。如需转载请注明出处: