单例模式总结

常用继承自monobehaviour的类的单例模式父类

using System;
using UnityEngine;


/// <summary>
/// Mono单例
/// </summary>
public abstract class MonoSingleton<T> : MonoBehaviour where T : MonoSingleton<T>
{
    private static T m_Instance;

    /// <summary>
    /// 单例对象 
    /// </summary>
    public static T Instance
    {
        get
        {
            if (m_Instance == null)
            {
                T[] ts = GameObject.FindObjectsOfType<T>();

                if (ts != null && ts.Length > 0)
                {
                    if (ts.Length == 1)
                    {
                        m_Instance = ts[0];
                    }
                    else
                    {
                        throw new Exception(string.Format("## Uni Exception ## Cls:{0} Info:Singleton not allows more than one instance", typeof(T)));
                    }
                }
                else
                {
                    m_Instance = new GameObject(string.Format("{0}(Singleton)", typeof(T).ToString())).AddComponent<T>();
                    m_Instance.OnSingletonInit();
                }
            }

            return m_Instance;
        }
    }

    protected MonoSingleton() { }

    protected virtual void Awake()
    {
        m_Instance = this as T;
        DontDestroyOnLoad(this.gameObject);
    }

    protected virtual void OnDestroy()
    {
        m_Instance = null;
    }

    protected virtual void OnSingletonInit()
    {
    }
}

非继承自其他类的单例模式写法

public class Singleton<T> where T : Singleton<T>{

    private static T _instance;

    public static T Instance
    {
        get {
            if (_instance == null)
            {
                _instance = Activator.CreateInstance<T>();
            }
            return _instance;
        }
    }
}

写法

public class HttpManager : Singleton<HttpManager>

关于C#的Lock关键字实现线程安全的单例模式

在看pureMVC使用的时候注意到有个关于线程安全的单例模式的使用用到了Lock关键词,这里对其进行一下记录。代码如下,


protected static readonly object m_staticSyncRoot;

public new static IFacade Instance
    {
        get
        {
            if(m_instance == null)
            {
                lock(m_staticSyncRoot)
                {
                    if (m_instance == null)
                    {
                        Debug.Log("ApplicationFacade");
                        m_instance = new ApplicationFacade();
                    }
                }
            }
            return m_instance;
        }
    }

这里用到了一个关键词lock,lock通过锁定一个实例,对一个代码段进行单线程的访问控制,即不论在任何时候只能有一个线程对lock大括号的代码段进行访问,其他线程运行到这个代码段的时候只能等待(或者说阻塞)在进入lock之前。
lock锁定一个对象的原理如下:

  • 对于任何一个对象,其在内存中的第一部分放置的是所有方法的地址,第二部分放着一个索引,索引指向CLR(公共语言运行时)中的SyncBlock
    Cache区域中的一个SyncBlock(SyncBlock简单来说就是Monitor.Enter用来锁定一个实例当前仅能被一个线程访问的工具).
  • Monitor.Enter和Monitor.Exit,这俩个函数是c#库的函数,他们的作用分别是锁定一个资源,使得其他线程不能被访问,以及解锁这个资源。当执行Monitor.Enter(object)时,如果object的索引值为负数,就从SyncBlock Cache中选区一个SyncBlock,将其地址放在object的索引中。这样就完成了以object为标志的锁定,其他的线程想再次进行Monitor.Enter(object)操作,将获得object为正数的索引,然后就等待。直到索引变为负数,即当前锁定线程使用Monitor.Exit(object)将object的索引变为负数。
  • lock语句根本使用的是Monitor.Enter和Monitor.Exit,即在大括号开始时对lock操作的对象传进Monitor.Enter,这里是m_staticSyncRoot,在大括号结束的时候再使用Monitor.Ente对m_staticSyncRoot进行解锁。

在上述代码中,lock的外部和内部使用到了两个看似重复的if判断,两者实际上都是有各自的功能的。
lock外部的if主要简单的对m_staticSyncRoot进行判空判断,决定是否要进入lock语句块。
在上文中黑体字突出的等待中可以发现,如果有多个线程同时执行上述代码,第一个线程进入lock块进行操作时,其余的线程都是在进入第一个if后进入lock之前等待,第一个进入lock块的线程进行初始化结束之后,其实这时m_staticSyncRoot已经指向某个实例了, 第二个进来的线程如果不进行判断,则还会再次进行实例化,所以在lock里面的if实际上是有用的。

关于lock的使用注意事项:

  • lock的对象不能是null,关于lock对一个对象进行上锁的机理中可以发现需要访问对象的内存的第二部分,而null值什么都没有,所以用null进行lock会报错。
  • 在使用lock的时候,被lock的对象(locker)一定要是引用类型的(即类的实例,在内存堆中生成,需要由垃圾回收机制进行销毁),如果是值类型(如int类型等基本类型,在内存栈中生成,使用完即刻销毁),将导致每次lock的时候都会将该值类型的对象装箱为一个新的引用对象(事实上如果使用值类型,C#编译器(3.5.30729.1)在编译时就会给出一个错误)。
  • 不要使用lock(this),this指的是当前的实例,当锁住后导致别的进程也无法访问整个实例,应lock一个专门用于lock的私有或者保护类型的成员变量或者私有或者保护类型的静态变量

其他注意点

Unity里面使用单例模式要注意一些其他事项。因为这是可能会导致bug的情况。对于使用MonoSingleTon的类,其一些初始化如果放在Awake或者Start里面,可能会导致还没初始,里面的内容就被调用,这发生在一开始没有挂载脚本的时候,一般情况下,例如枚举默认值赋值等,都可能会在这里栽倒。所以应该使用MonoSingleTon的方式来进行初始化,或者默认值直接写在成员变量赋值的部分.

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

染指流年丨

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值