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