单例模式再议
1.前言
曾多次对unity或者c#在使用单例时的一些问题进行过讨论,即有充满戾气的,也有理性的总结与封装(封装成一个抽象类,使用时直接继承),但多次使用时还是有一些不同的想法,故此文章诞生。此文将从纯C# 层展开。
2.单例的几种方式
此部分为纯C#层面
2.1 非线程安全模式
此种线程不安全,即多个线程同时初次调用单例时会产生空引用问题。
public sealed class Singleton
{
private static Singleton instance = null;
private Singleton(){}
public static Singleton instance
{
get
{
if (instance == null)
{
instance = new Singleton();
}
return instance;
}
}
}
2.2 简单线程安全模式
此方式通过锁实现线程安全,简单有效,但是由于获取单例时每次都需要走锁,会有一定的性能问题(普通应用下可以忽略不记)。
public sealed class Singleton
{
private static Singleton instance = null;
private static readonly object padlock = new object();
Singleton(){}
public static Singleton Instance
{
get
{
lock (padlock)
{
if (instance == null)
{
instance = new Singleton();
}
return instance;
}
}
}
}
关于锁的问题可以参考:https://www.cnblogs.com/wolf-sun/p/4209521.html
2.3 双重验证线程安全模式
此种方式实在上一种方式中在过锁前加了一个判断,来避免多次过锁。
public sealed calss Singleton
{
private static Singleton instance = null;
private static readonly object padlock = new object();
Singleton()
{
}
public static Singleton Instance
{
get
{
if (instance == null)
{
lock (padlock)
{
if (instance == null)
{
instance = new Singleton();
}
}
}
return instance;
}
}
}
2.4 非锁模式的线程安全
即利用C# 静态方法或变量的特性,实现线程安全,即保证所有的线程在调用时,单例已经生成,不需要判断单例为空。此方案在unity中曾多次使用,如此文描述,但也会存在一些问题。
public sealed class Singleton
{
//在Singleton第一次被调用时会执行instance的初始化
private static readonly Singleton instance = new Singleton();
//Explicit static consturctor to tell C# compiler
//not to mark type as beforefieldinit
static Singleton()
{
}
private Singleton()
{
}
public static Singleton Instance
{
get
{
return instance;
}
}
}
2.5 其他模式
还有一些其他模式如完全延时加载以及Lazy等,再次不再赘述。
3.Unity单例模式
3.1 特殊性
在unity中使用单例时,有如下三个特殊性:
1)线程安全性。由于涉及渲染对象操作都是在主线程中,所以除数据类问题、或者纯C#层问题外,基本都是单线程,所以在很大程度上不用考虑单例的线程安全性。
2)脚本的挂载操作。由于unity中使用类时并非直接使用或者new出一个对象,所以即使使用单例也会存在不同游戏物体上会挂载多个单例类(手误操作或者多人协作等导致)。
3)初始化问题。数据初始化一般都在MonoBehaviour的Awake或者Start中进行,这导致一些问题不同于C#原生单例。
3.2 一个单例
回归到本质,在unity中要防止为了使用单例而使用,要做好前期规划,由于种种问题,很难做到两全,所以做好预防处理即可。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class XXXManager : MonoBehaviour
{
private static XXXManager instance;
public static XXXManager GetInstance()
{
if (instance == null)
{
Debug.LogError("Null reference of XXXManager instance");
}
return instance;
}
private void Awake()
{
if (instance != null)
{
Debug.Log("Multiple instance of XXXManager");
}
else
{
instance = this;
}
}
}
4.结论
无结论