资讯动态

单例模式总结 MonoSingleton

发布时间:2026/9/5 14:58:45 来源:尧图企业网站定制
常用继承自monobehaviour的类的单例模式父类usingSystem;usingUnityEngine;/// summary/// Mono单例/// /summarypublicabstractclassMonoSingletonT:MonoBehaviourwhereT:MonoSingletonT{privatestaticTm_Instance;/// summary/// 单例对象/// /summarypublicstaticTInstance{get{if(m_Instancenull){T[]tsGameObject.FindObjectsOfTypeT();if(ts!nullts.Length0){if(ts.Length1){m_Instancets[0];}else{thrownewException(string.Format(## Uni Exception ## Cls:{0} Info:Singleton not allows more than one instance,typeof(T)));}}else{m_InstancenewGameObject(string.Format({0}(Singleton),typeof(T).ToString())).AddComponentT();m_Instance.OnSingletonInit();}}returnm_Instance;}}protectedMonoSingleton(){}protectedvirtualvoidAwake(){m_InstancethisasT;DontDestroyOnLoad(this.gameObject);}protectedvirtualvoidOnDestroy(){m_Instancenull;}protectedvirtualvoidOnSingletonInit(){}}非继承自其他类的单例模式写法publicclassSingletonTwhereT:SingletonT{privatestaticT_instance;publicstaticTInstance{get{if(_instancenull){_instanceActivator.CreateInstanceT();}return_instance;}}}写法publicclassHttpManager:SingletonHttpManager关于C#的Lock关键字实现线程安全的单例模式在看pureMVC使用的时候注意到有个关于线程安全的单例模式的使用用到了Lock关键词这里对其进行一下记录。代码如下protectedstaticreadonlyobjectm_staticSyncRoot;publicnewstaticIFacadeInstance{get{if(m_instancenull){lock(m_staticSyncRoot){if(m_instancenull){Debug.Log(ApplicationFacade);m_instancenewApplicationFacade();}}}returnm_instance;}}这里用到了一个关键词locklock通过锁定一个实例对一个代码段进行单线程的访问控制即不论在任何时候只能有一个线程对lock大括号的代码段进行访问其他线程运行到这个代码段的时候只能等待或者说阻塞在进入lock之前。lock锁定一个对象的原理如下对于任何一个对象其在内存中的第一部分放置的是所有方法的地址,第二部分放着一个索引,索引指向CLR公共语言运行时中的SyncBlockCache区域中的一个SyncBlockSyncBlock简单来说就是Monitor.Enter用来锁定一个实例当前仅能被一个线程访问的工具.Monitor.Enter和Monitor.Exit这俩个函数是c#库的函数他们的作用分别是锁定一个资源使得其他线程不能被访问以及解锁这个资源。当执行Monitor.Enterobject时如果object的索引值为负数就从SyncBlock Cache中选区一个SyncBlock将其地址放在object的索引中。这样就完成了以object为标志的锁定其他的线程想再次进行Monitor.Enter(object)操作将获得object为正数的索引然后就等待。直到索引变为负数即当前锁定线程使用Monitor.Exitobject)将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)在编译时就会给出一个错误)。不要使用lockthisthis指的是当前的实例当锁住后导致别的进程也无法访问整个实例应lock一个专门用于lock的私有或者保护类型的成员变量或者私有或者保护类型的静态变量其他注意点Unity里面使用单例模式要注意一些其他事项。因为这是可能会导致bug的情况。对于使用MonoSingleTon的类其一些初始化如果放在Awake或者Start里面可能会导致还没初始里面的内容就被调用这发生在一开始没有挂载脚本的时候一般情况下例如枚举默认值赋值等都可能会在这里栽倒。所以应该使用MonoSingleTon的方式来进行初始化或者默认值直接写在成员变量赋值的部分.

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价