zl程序教程

您现在的位置是:首页 >  后端

当前栏目

单例模式(Singleton)

模式 单例 Singleton
2023-06-13 09:15:37 时间

意图

单例模式是一种创建型模式,它能确保一个类只有一个实例,并提供一个访问该实例的全局节点。

问题

  1. 单例模式同时解决了两个问题, 所以违反了单一职责原则: 保证一个类只有一个实例。 为什么会有人想要控制一个类所拥有的实例数量? 最常见的原因是控制某些共享资源(例如数据库或文件)的访问权限。 它的运作方式是这样的: 如果你创建了一个对象, 同时过一会儿后你决定再创建一个新对象, 此时你会获得之前已创建的对象, 而不是一个新对象。 注意, 普通构造函数无法实现上述行为, 因为构造函数的设计决定了它必须总是返回一个新对象。
  1. 为该实例提供一个全局访问节点。想想你曾用过的那些存储重要对象的全局变量,它们在使用上十分方便, 但同时也非常不安全,因为任何代码都有可能覆盖掉那些变量的内容,从而引发程序崩溃。 和全局变量一样, 单例模式也允许在程序的任何地方访问特定对象。 但是它可以保护该实例不被其他代码覆盖。 还有一点: 你不会希望解决同一个问题的代码分散在程序各处的。 因此更好的方式是将其放在同一个类中, 特别是当其他代码已经依赖这个类时更应该如此。

解决方案

一个类的对象永远只会在当前进程中被创建一次,也就是说构造函数只可能被调用一次,不论有多少线程调用。为什么需要单例,假如这个类是用来操作某个资源的,如果存在多个这个类的实例,这可能在操作这个资源的时候造成破坏,所以只能创建一个实例是很有必要的。

所有单例的实现都包含以下两个相同的步骤:

  1. 将默认构造函数设为私有, 防止其他对象使用单例类的new运算符。
  2. 新建一个静态构建方法作为构造函数。 该函数会 “偷偷” 调用私有构造函数来创建对象, 并将其保存在一个静态成员变量中。 此后所有对于该函数的调用都将返回这一缓存对象。

如果你的代码能够访问单例类, 那它就能调用单例类的静态方法。 无论何时调用该方法, 它总是会返回相同的对象。

结构

  1. 单例(Singleton)类声明了一个名为get­Instance获取实例的静态方法来返回其所属类的一个相同实例。 单例的构造函数必须对客户端(Client)代码隐藏。 调用获取实例方法必须是获取单例对象的唯一方式。

实现方式

  1. 在类中添加一个私有静态成员变量用于保存单例实例。
  2. 声明一个公有静态构建方法用于获取单例实例。
  3. 在静态方法中实现”延迟初始化”。该方法会在首次被调用时创建一个新对象,并将其存储在静态成员变量中。此后该方法每次被调用时都返回该实例。
  4. 将类的构造函数设为私有。 类的静态方法仍能调用构造函数, 但是其他对象不能调用。
  5. 检查客户端代码, 将对单例的构造函数的调用替换为对其静态构建方法的调用。

代码演示

一般来说,直接把对象声明为静态即可,程序集在加载过程中进行构造,这个也是线程安全的。但问题是如果此对象一直没有被调用,同时构造函数的开销较大,这个会造成资源浪费。

1234567891011121314151617

class Singleton{ private static Singleton instance = new Singleton(); /// <summary> /// 构造函数声明为私有 /// </summary> private Singleton() { Console.WriteLine("执行构造函数"); } public static Singleton Get() { return instance; }}

著名的双检锁法,只在需要时执行构造函数,同时也是线程安全的

123456789101112131415161718192021222324252627282930313233

class Singleton{ private static Singleton instance = null; private static readonly object lockobj = new object(); /// <summary> /// 构造函数声明为私有 /// </summary> private Singleton() { Console.WriteLine("执行构造函数"); } /// <summary> /// 双检锁 /// </summary> /// <param name="owner"></param> /// <returns></returns> public static Singleton Get() { if (instance == null) { lock (lockobj) { if (instance == null) { instance = new Singleton(); } } } return instance; }}

线程安全单例的完整代码示例:

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788

using System;using System.Threading;namespace Singleton{ // This Singleton implementation is called "double check lock". It is safe // in multithreaded environment and provides lazy initialization for the // Singleton object. class Singleton { private Singleton() { } private static Singleton _instance; // We now have a lock object that will be used to synchronize threads // during first access to the Singleton. private static readonly object _lock = new object(); public static Singleton GetInstance(string value) { // This conditional is needed to prevent threads stumbling over the // lock once the instance is ready. if (_instance == null) { // Now, imagine that the program has just been launched. Since // there's no Singleton instance yet, multiple threads can // simultaneously pass the previous conditional and reach this // point almost at the same time. The first of them will acquire // lock and will proceed further, while the rest will wait here. lock (_lock) { // The first thread to acquire the lock, reaches this // conditional, goes inside and creates the Singleton // instance. Once it leaves the lock block, a thread that // might have been waiting for the lock release may then // enter this section. But since the Singleton field is // already initialized, the thread won't create a new // object. if (_instance == null) { _instance = new Singleton(); _instance.Value = value; } } } return _instance; } // We'll use this property to prove that our Singleton really works. public string Value { get; set; } } class Program { static void Main(string[] args) { // The client code. Console.WriteLine( "{0}\n{1}\n\n{2}\n", "If you see the same value, then singleton was reused (yay!)", "If you see different values, then 2 singletons were created (booo!!)", "RESULT:" ); Thread process1 = new Thread(() => { TestSingleton("FOO"); }); Thread process2 = new Thread(() => { TestSingleton("BAR"); }); process1.Start(); process2.Start(); process1.Join(); process2.Join(); } public static void TestSingleton(string value) { Singleton singleton = Singleton.GetInstance(value); Console.WriteLine(singleton.Value); } }}

执行结果:

12

FOOFOO

参考原文:单例设计模式