单例模式是所有设计模式中最简单的一种;
1.定义
定义起来比较简单,就是 应用该模式的类在系统中最多只能存在一个实例。
结构图如下,抄自 链接
2.实现方式
要实现这种模式,方法有很多种,我这里只介绍最常见和常用的(常见不一定常用哦~);
2.1 饿汉式
/// <summary> /// 饿汉式 /// </summary> public class Singleton1 { private readonly static Singleton1 instance = new Singleton1(); private Singleton1() { } public static Singleton1 GetInstance() { return instance; } }
优点:简单,多线程安全
缺点:如果该单例从头到尾没有用到的话,还是会被实例化,资源被浪费
2.2 懒汉式
/// <summary> /// 懒汉式 /// </summary> public class Singleton2 { private static Singleton2 instance; private Singleton2() { } public static Singleton2 GetInstance() { if (instance == null) { instance = new Singleton2(); } return instance; } }
优点:资源不会浪费
缺点:线程不安全(多线程环境下需要注意)
2.3 双重检查式
/// <summary> /// 双重检查 /// </summary> public class Singleton3 { private static Singleton3 instance; private Singleton3() { } /// <summary> /// 同步锁 /// </summary> private static readonly object locker = new object(); public static Singleton3 GetInstance() { if (instance == null) { lock (locker) { if (instance == null) { instance = new Singleton3(); } } } return instance; } }
在很多讲单例模式的文章中,这种方式都是最被推崇的,因为首先不会浪费资源,而且还线程安全,为什么要双重检查,这主要是避免资源总被锁定,节省资源。
优点:节省资源,线程安全
缺点:getInstance方法略微有点啰嗦
2.4 懒加载式(Lazy)
我个人比较喜欢这种,因为C#提供的Lazy关键字语法,使得我们可以延迟加载单例的实例,而且写出来没有双重检测那么啰嗦。
/// <summary> /// 懒加载方式 /// </summary> public class Singleton4 { private static Lazy<Singleton4> instance = new Lazy<Singleton4>(() => new Singleton4()); private Singleton4() { } public static Singleton4 GetInstance() { return instance.Value; } }
使用了Lazy关键字,则instance对象只有在调用Value属性的时候才会实例化,详细解释各位看官可以去查查官网对Lazy的解释。
优点:节省资源,线程安全
缺点:暂无
3.单例模式的用处以及特点
3.1 用到的地方
在实现 生产消费者模式 的时候,那么我们只有保持操作的是同一个实例,这样才能保证要消费的产品不会被漏掉,详细见代码();
代码实现的是多线程安全的Log操作,使用的生成消费者模式,这里单例的实现是使用双重检测的方式,但是换成Lazy延迟加载方式也是完全正确的。
public class LogHelper { #region 私有成员 & 构造方法 private static readonly ConcurrentQueue<string> queue = new ConcurrentQueue<string>(); private static LogHelper instance; private AutoResetEvent @event = new AutoResetEvent(true); private static string configLogPath = ConfigurationManager.AppSettings["logpath"]; private static string logDirectoryPath = string.IsNullOrEmpty(configLogPath) ? "d:/csts-log/" : configLogPath.ToString(); private static readonly object locker = new object(); private LogHelper() { Task.Run(() => { QueueWriteLog(); }); } #endregion #region 公共接口 public static void WriteLine(string str) { var value = GetInstance(); if (value == null) { return; } value.Enqueue(str); } #endregion #region 私有方法 private static LogHelper GetInstance() { if (null == instance) { lock (locker) { if (null == instance) { instance = new LogHelper(); } } } return instance; } private void Enqueue(string str) { queue.Enqueue(str); @event.Set(); } private void QueueWriteLog() { while (true) { if (queue.IsEmpty) { @event.WaitOne(); } string logStr; if (queue.TryDequeue(out logStr)) { WriteText(logStr); } } } private void WriteText(string str) { if (!Directory.Exists(logDirectoryPath)) { Directory.CreateDirectory(logDirectoryPath); } var logPath = logDirectoryPath + "/" + DateTime.Now.ToString(FormatType.Date) + ".txt"; using (StreamWriter sw = new StreamWriter(logPath, true)) { sw.WriteLine(str); } } #endregion }
3.2 特点
一般我觉得单例模式在很多时候和其他许多设计模式不一样的一点在于,其他的设计模式用出来会让后期功能的添加更方便快捷,但是单例模式我个人觉得就是功能需求,逼迫你必须在环境下只存在一个实例,这个时候就必须要用了。