double check locking in singleton pattern
这可能是个基本问题
要在多线程环境中使用单实例,我们可以使用锁。请参考代码段。但是为什么我们需要单例模式中的双重检查锁定?还有,双重检查锁定意味着什么?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | class singleton { private static singleton instance = null; private static singleton() { } private static object objectlock = new object(); public static singleton Instance { get { lock (objectlock) //single - check lock { if (instance == null) { instance = new singleton(); } return instance; } } } } |
乔恩·斯基特对此作了详细解释。
锁很贵。如果对象已经存在,就没有必要取出锁。因此,您可以在锁外进行第一次检查。
但是,即使在查看之前对象不存在,另一个线程也可能在
然而,编写singleton的最佳方法是使用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | public sealed class Singleton { private Singleton() { } public static Singleton Instance { get { return Nested.instance; } } private class Nested { // Explicit static constructor to tell C# compiler // not to mark type as beforefieldinit static Nested() { } internal static readonly Singleton instance = new Singleton(); } } |
对于.NET 4.x及更高版本,您应该尽可能地遵从lazy类,因为此模式与初始化和发布选项一起使用。(注意:如果创建不是线程安全的,但实例的发布是通过发布选项发布的,则可以使用反向函数)
多线程单线程:使用双重检查锁定的最佳方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | public sealed class Singleton { private static volatile Singleton _instance; private static readonly object InstanceLoker= new Object(); private Singleton() {} public static Singleton Instance { get { if (_instance == null) { lock (InstanceLoker) { if (_instance == null) _instance = new Singleton(); } } return _instance; } } } |
我知道"最好"的方法是:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 | public class MySingleton { // object for synchronization private static readonly object syncRoot = new object(); // the singleton instance private static MySingleton @default; public static MySingleton Default { get { // geting singleton instance without locking var result = @default; // if result is NOT null, no additional action is required if ( object.ReferenceEquals(result, null) ){ // lock the synchronization object lock(syncRoot) { // geting singleton instanc in lock - because // the value of @default field could be changed result = @default; // checking for NULL if ( object.ReferenceEquals(result, null) ) { // if result is NULL, create new singleton instance result = new MySingleton(); // set the default instance @default = result; } } } // return singleton instance return result; } } } |
当我们尝试使用并行库执行singleton类的方法时。由于在TPL中执行多线程导致概念单例模式失败,因此无法识别单例行为。为了解决这个问题,有一个锁定对象的概念,这样一次只有一个线程可以访问它。但这不是有效的方法,因为涉及锁检查会为对象创建不必要的监视。为了避免这种情况,我们使用"双重锁定检查"。
如果在字段初始化器中创建对象,则不需要锁:
1 2 3 4 5 6 7 8 9 10 | class singleton { private static singleton instance = new singleton(); private static singleton() { } public static singleton Instance { get { return instance; } } } |
还要记住,锁只控制对象的创建,如果在多个线程中使用对象,那么对象仍然需要是线程安全的。