单例模式——多线程——双重锁定

多线程的程序中,多个线程同时,注意是同时访问Singleton类,调用GetInstance()方法,会有可能造成多个实例的。这个时候需要给进程加锁来处理

Singleton类

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Singleton_Model
{
    class Singleton
    {
        //私有化一个自己引用
        private static Singleton instance;
        //程序运行时候创建一个静态只读的进程辅助对象
        private static readonly object syncRoot = new object();
        //私有化构造器 杜绝了外界利用New实例化本类的可能
        private Singleton()
        {

        }
        //此方法是获得本类实例的唯一全局访问点
        public static Singleton GetInstance()
        {
            lock (syncRoot)
            {
                if (instance == null)
                {
                    instance = new Singleton();
                }
            }
            
            return instance;
        }
    }
}

缺点:每次调用Instance都需要Lock会很耗费性能

新的Singleton类

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Singleton_Model
{
    class Singleton
    {
        //私有化一个自己引用
        private static Singleton instance;
        //程序运行时候创建一个静态只读的进程辅助对象
        private static readonly object syncRoot = new object();
        //私有化构造器 杜绝了外界利用New实例化本类的可能
        private Singleton()
        {

        }
        //此方法是获得本类实例的唯一全局访问点
        public static Singleton GetInstance()
        {
            if (instance==null)
            {
                lock (syncRoot)
                {
                    if (instance == null)
                    {
                        instance = new Singleton();
                    }
                }
            }
            
            
            return instance;
        }
    }
}

这里可以看到里外两层判断!没错这里没有问题 !当Instance为null并且同时有两个线程调用GetInstance方法时,它们都将可以通过第一重的Instancenull的判断,然后由于Lock机制,这两个线程则只能有一个进入 另一个排队等候。此时若是没有第二重的Instancenull的判断,则第一个线程创建了实例,而第二个线程还是可以继续再创建新的实例,这样就没有达到单例的目的!!

静态初始化:其实在实际的应用当中,C#与公共语言运行库也提供了一种‘静态初始化’的方法,这种方法不需要开发人员显示的编写线程安全的代码,即可以解决多线程环境下它不是安全的问题!实现更简单,但是谈不上更好!

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Singleton_Model
{
    //sealed阻止发生派生,而派生可能会增加实例。
    public sealed class Singleton1
    {
        private static readonly Singleton1 instance = new Singleton1();
        private Singleton1()
        {

        }
        public static Singleton1 GetInstance()
        {
            return instance;
        }
        /**
         * 
         * * 这样的实现与前面的实例类似,也是解决了单例模式试图解决的两个基本问题,全局访问和实例化控制,
         *  1 公共静态属性为访问实例提供了一个全局的访问点。
         *  不同之处在于它依赖公共语言运行库来初始化变量。由于构造方法是私有的,因此不能再类本身以外实例化
         *  Singleton1类。因此 变量引用的是可以在系统中存在的唯一的实例。不过要注意,instance变量标记为readonly
         *  这意味着只能在静态初始化期间或在类构造函数中分配【MSDN】。
         *  由于这种静态初始化的方式是在自己被加载时就将自己实例化 所以形象的称为饿【汉式单例模式】
         *  原先的单例处理模式是要再第一次被引用时,才会将自己实例化 所以被形象称为【懒汉单例模式】
         * 【汉式单例模式】 静态初始化方式,它是类一加载就实例化的对象,所以要提前占用系统资源
         * 【懒汉单例模式】 面临着多线程访问的安全问题 需要做双重锁定才能保证安全。
         * 所以要用哪一种 必须考虑实际的需求 C#【汉式单例模式】基本能满足需求
         */
    }
}

这样的实现与前面的实例类似,也是解决了单例模式试图解决的两个基本问题,全局访问和实例化控制,
* 1 公共静态属性为访问实例提供了一个全局的访问点。
* 不同之处在于它依赖公共语言运行库来初始化变量。由于构造方法是私有的,因此不能再类本身以外实例化
* Singleton1类。因此 变量引用的是可以在系统中存在的唯一的实例。不过要注意,instance变量标记为readonly
* 这意味着只能在静态初始化期间或在类构造函数中分配【MSDN】。
* 由于这种静态初始化的方式是在自己被加载时就将自己实例化 所以形象的称为饿【汉式单例模式】
* 原先的单例处理模式是要再第一次被引用时,才会将自己实例化 所以被形象称为【懒汉单例模式】
* 【汉式单例模式】 静态初始化方式,它是类一加载就实例化的对象,所以要提前占用系统资源
* 【懒汉单例模式】 面临着多线程访问的安全问题 需要做双重锁定才能保证安全。
* 所以要用哪一种 必须考虑实际的需求 C#【汉式单例模式】基本能满足需求

原创文章 37 获赞 11 访问量 2765

猜你喜欢

转载自blog.csdn.net/qq_39691716/article/details/104288350