游戏引擎:Unity 版本:2019.4.6f1 【2017版本以上均可】 编译平台:Visual Studio 2019
一、了解单例单例模式可以保证系统中,应用该模式的类一个类只有一个实例。即一个类只有一个对象实例。单例模式是设计模式中最简单的形式之一。这一模式的目的是使得类的一个对象成为系统中的唯一实例。
二、思路单例Singleton遵从以下顺序(由1至4):
1234构造函数私有化提供静态类自身的只读属性创建对象实例方法注:以下脚本按照有缺点进行优化,具体见每类型最后一项。
2.1 饿汉写法 2.1.0 什么是饿汉写法?答:创建对象的时机最早。
2.1.1 代码块class Singleton
{
//1.构造函数私有化
private Singleton() { }
//2.提供静态自身类型的只读属性
public static Singleton Instance { get; private set; }
//3.创建对象
static Singleton()
{
Instance = new Singleton();
}
//4.实例方法
public void Test() { }
}
2.1.2 缺点
在【流程3】过程中,由于在静态构造函数中创建对象(属性赋值),可能时机过早。这里的时机过早——例如我只想访问静态类成员变量,造成在单例Singleton中必须过早的创建实例对象。最终造成的不必要的性能开支。
2.2 饿汉写法(改进)按需加载思想:需要时加载,不需要不加载。改进后的代码按照你需要访问属性,我创建对象;不需要则不用创建。如果我没有实例,则创建;有之则返回属性,不需反复创建。
2.2.1 代码块class Singleton
{
//1.构造函数私有化
private Singleton() { }
#region 2.创建对象-按需加载
//2.静态类自身字段
private static Singleton instance;
//2.静态类自身属性
public static Singleton Instance
{
get
{
if (instance == null) { instance = new Singleton(); }
return instance;
}
}
#endregion
//3.实例方法
public void Test() { }
}
2.2.2 缺点
if (instance == null) { instance = new Singleton(); }
因为是按需加载,在多线程中,可能多个线程同时访问Instance属性,造成同时满足条件并创建对象。
2.3 饱汉写法 2.3.0 什么是饱汉写法?答:创建对象的时机最晚。
2.3.1 代码块多线程遇到lock会自动停止,等待前一个线程结束后执行。避免多个线程的同时执行。
class Singleton02
{
private static object locker = new object();
//1.构造函数私有化
private Singleton02() { }
//2.静态自身类型字段
private static Singleton02 instance;
//2.静态自身类型属性
public static Singleton02 Instance
{
get
{
//加锁
lock (locker)
{
//缺点:每次访问Instance属性,都会判断线程锁,造成读取缓慢
if (instance == null) { instance = new Singleton02(); }
return instance;
}
}
}
//3.实例方法
public void Test() { }
}
2.3.2 缺点
每次访问需要进行一次 lock(locker) 的判断,存在不必要的性能消耗。
2.4 饱汉写法(改进) 2.4.1 代码块双重检测机制:第一次判断为null,去new一个对象后,第二次判断就不会null。
class Singleton02
{
private static object locker = new object();
//1.构造函数私有化
private Singleton02() { }
//2.静态自身类型字段
private static Singleton02 instance;
//2.静态自身类型属性
public static Singleton02 Instance
{
get
{
//双重检测
//第一层:避免每次访问属性都判断线程锁
if (instance == null)
{
lock (locker)
{
//第二层:避免重复创建对象
if (instance == null) { instance = new Singleton02(); }
}
}
return instance;
}
}
//3.实例方法
public void Test() { }
}
三、为什么要选择单例类?而不是静态类
单例类静态类游戏对象可创建(附加于)实例成员可定义不可定义接口可实现不可实现
四、更多说明
文章如有错误,请联系笔者修正。