多线程环境下的单件模式
其实在上面的讨论中,笔者并没有对多线程应用的单件模式保护进行讨论。可以说即便有了私有的构造函数,即便避免了ICloneable接口和SerializableAttribute属性的错误使用,一样会因为并发的instance == null错误判断导致多个instance = new Singleton()语句的执行,最终形成多个实例的出现,为此需要在访问instance == null之前需要增加一个锁来限制单件构造函数的构造过程。
using System; namespace VisionLogic.DesignPattern.Practice ...{ public class Singleton ...{ private static volatile Singleton instance; private static object root = new object(); private Singleton() ...{ } public static Singleton Instance ...{ get ...{ lock (root) ...{ if (instance == null) instance = new Singleton(); } return instance; } } } }
示例9
这里通过简单增加一个lock(root)判断来保证即便运行在多线程环境下,即便并发的调用进入了通过Instance静态属性获得单件引用的情况下,Singleton实例的构造过程唯一性。
但是,这个实现其时存在很大的性能隐患,尤其对于频繁短调用的应用情形而言,一个锁把整个系统的调用归为串行,不管设备节点有多少个N核的CPU。应用其他部分有多大的并发可能执行到这里只能串行,一个锁成了整个应用的瓶颈。为了避免这个情况的出现,需要在lock之前再为其增加一个instance == null的检查,保证instance一旦建立,后面的客户程序调用可以继续以并发方式执行。或者就彻底把这个唯一instance的构造过程通过readonly限制放在一个只会唯一执行一次的某个过程中——Singleton类的静态构造函数中。
using System; namespace VisionLogic.DesignPattern.Practice ...{ public class Singleton ...{ private static volatile Singleton instance; private static object root = new object(); private Singleton() ...{ } public static Singleton Instance ...{ get ...{ if (instance == null) ...{ lock (root) ...{ if (instance == null) instance = new Singleton(); } } return instance; } } } }
示例10:通过两次instace == null检查保证高效的单件实例获取
using System; namespace VisionLogic.DesignPattern.Practice ...{ public class Singleton ...{ public static readonly Singleton Instance = new Singleton(); } }
示例11:微软MSDN站点上提供的通过静态只读实例方式实现的多线程安全的单件模式
后者虽然在实现代码上仅仅简化为一句,但是通过分析其IL就可以了解他为什么可以保证即便在多线程环境下仍然可以保证单件。
图:通过只读静态构造函数完成的单件模式编译结果
其中,请注意.class public auto ansi beforefieldinit部分,它等于CLR内置的静态构造机制帮助我们完成了加锁过程,确保了只有静态的对象在默认提供的静态构造器创建之前,实例的所有属性不能够被访问。而唯一实例静态的Instance则可以安安稳稳地在默认的静态构造器中创建。下面是编译器生成的静态构造函数。
.method private hidebysig specialname rtspecialname static void .cctor() cil managed ...{ // Code size 11 (0xb) .maxstack 8 IL_0000: newobj
instance void VisionLogic.DesignPattern.Practice.Singleton::.ctor() IL_0005: stsfld
class VisionLogic.DesignPattern.Practice.Singleton
VisionLogic.DesignPattern.Practice.Singleton::Instance IL_000a: ret } // end of method Singleton::.cctor