以下代码摘自该文章:
using System; public sealed class Singleton { private static volatile Singleton instance; private static object syncRoot = new object(); private Singleton() {} public static Singleton Instance { get { if (instance == null) { lock (syncRoot) { if (instance == null) instance = new Singleton(); } } return instance; } } }
具体来说,在上述示例中,是否需要在锁之前和之后两次将实例比较为null?这有必要吗?为什么不先执行锁定并进行比较?
简化为以下内容是否有问题?
public static Singleton Instance { get { lock (syncRoot) { if (instance == null) instance = new Singleton(); } return instance; } }
执锁昂贵吗?
与简单指针检查相比,执行锁定 非常 昂贵instance != null。
instance != null
您在此处看到的模式称为“ 双重检查锁定”。它的目的是避免只需要一次(第一次访问单例时)的昂贵的锁定操作。之所以这样实现是因为它还必须确保在初始化单例时不会有线程争用条件导致的错误。
可以这样考虑:仅当答案为“是,对象已被构造”时,才可以进行裸null检查(不带lock),以提供正确的可用答案。但是,如果答案是“尚未构建”,那么您将没有足够的信息,因为您真正想知道的是它“尚未构建, 并且没有其他线程打算在不久后构建它 ”。因此,您将外部检查用作非常快速的初始测试,并且仅在答案为“否”的情况下,才启动正确的,无错误但“昂贵的”过程(锁定然后检查)。
null
lock
The above implementation is good enough for most cases, but at this point it’s a good idea to go and read Jon Skeet’s article on singletons in C# which also evaluates other alternatives.