Fra*_* Q. 4 .net c# multithreading volatile memory-barriers
这是我的代码,运行几个月没有任何问题.
public sealed class Singleton
{
private static Singleton value;
private static object syncRoot = new Object();
public static Singleton Value
{
get
{
if (Singleton.value == null)
{
lock (syncRoot)
{
if (Singleton.value == null)
{
Singleton.value = new Singleton();
}
}
}
return Singleton.value;
}
}
}
Run Code Online (Sandbox Code Playgroud)
但是,我遇到了这个链接,并概述了上述问题.
a)写入Singleton.value = new Singleton();可能会缓存在处理器上,因此其他线程可能不会最终看到它.要volatile使用,请使用此关键字.
问(1):C#lock关键字不能解决这个问题吗?
b)在同一篇文章中概述的另一个更好的解决方案是在写入之后避免volatile和引入.System.Threading.Thread.MemoryBarrier();Singleton.value
问题:
问(2)我不太明白MemoryBarrier()写完后的必要性.什么可能的重新排序可能会导致其他线程看到Singleton.valuenull?在lock阻止其他线程甚至读什么.
Q(3)障碍只会维持秩序,但如果仍然从某些缓存中读取该值,该怎么办呢?是不是仍然需要挥发性?
Q(4)因为C#lock本身就是它所需要的屏障吗?
最后,我是否需要使用任何一种方法更新我的代码或者它是否足够好?
编辑
有一个答案建议使用Lazy初始化.我知道了.
但他们试图使用volatie和memorybarrier来实现锁定并不能保证什么?
这是我的代码,运行几个月没有任何问题.
如果有一个十亿分之一的失败机会,并且代码在一千台机器上每天运行一千次,那么平均每三年就有一次不可能调试的关键故障.
如果它只在特定硬件上失败,并且您在x86上进行了所有测试,那么您将永远不会看到失败.
没有测试低锁代码的正确性的事情.代码可以证明是正确的,或者不是.你不能依赖测试.
C#lock关键字不能解决这个问题吗?
在其中一个读数上省略了锁定.
锁可以防止其他线程甚至读取任何内容.
在其中一个读数上省略了锁定.
障碍只会维持秩序,但如果仍然从某些缓存中读取值,该怎么办呢.是不是仍然需要挥发性?
从缓存中读取相当于及时向后移动读取; 由挥发性或显性障碍引起的障碍限制了如何观察这种向后运动.
是否真的需要屏障,因为C#锁定自己放置它?
在其中一个读数上省略了锁定.
我是否需要使用任何一种方法更新我的代码,还是足够好?
我永远不会写这样的代码.如果您需要延迟初始化,请使用Lazy<T>.如果需要单例,请使用单例模式的标准实现.不要自己解决这些问题; 让专家为您解决这些问题.
但他们试图使用volatile和memorybarrier来实现锁定并不能保证什么?
他们试图正确地忽略锁定,从而在非竞争路径中节省几纳秒.与难以调试的罕见关键故障的成本相比,这些纳秒对您的用户有多大价值?
每当你试图忽略锁定时,你就完全沉浸在低级记忆模型的疯狂世界中.你必须假设所有的记忆都在不断变化,除非有什么东西保持不变; 您必须假设存储器访问的任何和所有合法重新排序都是可能的,即使在大多数硬件上也是不可能的.您不知道将来会发明什么奇怪的硬件并用于运行您的代码.
不要去那里.我不喜欢使用线程; 如果我想并行化某些东西,我的偏好是在问题上抛出虚拟机,容器或进程.如果必须使用线程,请尝试不共享内存.如果你有共享存储,使用由专家建立的最高级别的结构,像Task和Lazy,而不是滚动您自己出的记忆障碍和联锁操作.