来自:阿里的 通义灵码
在C#中,单例模式实现线程安全时通常会采用双重检查锁定(Double-Checked Locking, DCL)的策略来确保只创建类的一个实例,并且这个过程是线程安全的。
两层lock
的设计是为了减少获取锁的开销,尤其是当类的实例已经被初始化的情况下。
以下是一个使用双重检查锁定的C#单例模式示例:
public sealed class Singleton { // 创建一个静态变量来保存唯一的 Singleton 实例。 private static volatile Singleton instance; // 创建一个对象用于同步访问Singleton实例。 private static object syncRoot = new Object(); // 私有构造函数防止其他代码通过new关键字实例化。 private Singleton() {} // 提供一个公共的静态方法来返回 Singleton 实例。 public static Singleton Instance { get { // 第一层检查:如果实例已存在,则直接返回,无需加锁。 if (instance != null) { return instance; } // 第二层检查:只有当第一次进入这里,即实例未被初始化时才需要加锁。 lock (syncRoot) { // 在锁内再次检查实例是否已被另一个线程初始化。 // 这是因为,在第一个if判断和进入lock之间可能存在竞态条件。 if (instance == null) { // 只有在这里创建实例。 instance = new Singleton(); } return instance; } } } }
为什么需要两层检查?
- 性能优化:
- 第一次检查
instance != null
可以在不加锁的情况下完成,如果实例已经初始化,那么大多数情况下可以直接返回实例,避免了不必要的锁操作带来的性能损耗。
- 第一次检查
- 线程安全性:
- 如果多个线程同时发现实例尚未创建,而只有一个线程能够进入内部的
lock
块进行初始化。如果没有第二次检查,所有线程都会创建实例,导致违反单例原则。 - 由于CLR的内存模型允许对象的构造和初始化顺序与指令执行顺序不同步,因此即使在多线程环境中,也需要在
lock
保护下进行实例化以保证线程安全。第二层检查确保了实例化操作仅执行一次。
- 如果多个线程同时发现实例尚未创建,而只有一个线程能够进入内部的
总结来说,双重检查锁定的目的在于既保证了线程安全,又尽量减少了对锁的使用频率,从而提高了程序的并发性能。
标签:初始化,Singleton,检查,C#,模式,instance,实例,线程,单例 From: https://www.cnblogs.com/z5337/p/18090164