引子
假设现在要做一个“家庭管理系统”, 有个类名字叫做 “Wife”。
需要实例化“MyWife”,我就new一个出来;
Wife MyWife = new Wife(); //嘻嘻,属性挺多,方法么?洗衣做饭等等都有,但好像工作状态很不稳定
MyWife.Wash();
程序另外一处也需要"MyWife", 好办啊
Wife MyWife = new Wife();
MyWife.Cook();
嗯?这个老婆和上面的是不是一个呢?好像不能保证。现在咱们国家婚姻法规定了一夫一妻制度。所以老婆嘛,是只有一个的(多的那不叫老婆)。
所以,问题是,如何保证程序里,一个类(Wife)只有一个实例(MyWife), 并提供一个该实例的全局访问点?
模型图

单线程Singleton模式
使用statics约束构造函数,在公有属性中实现类的实例化。
Singleton pattern “structural example"
class Singleton
{
private static Singleton _instance;
private Singleton(){}
public static Singleton Instance
{
get
{
if(_instance == null) // 是为了 "lazy Initialization" , 程序启动时,不用的类就不会被new实例出来
{
_instance = new Singleton();
}
return _instance;
}
}
}
}
使用:
Singleton t1 = Singleton.Instance;
Singleton t2 = Singleton.Instance;
Console.WriteLine( Object.Equals( t1, t2 ) );
输出结果: true
说明只有一个实例。
上面的并不是线程安全的,两个线程同时访问到if(_instance==null),就可能最后出来两个实例。
简单Singleton在多线程下的失败
将Singleton的Instance改为:模仿构造函数比较耗时,
if (instance == null)
{
Thread.Sleep(1000);
//多个线程start后,若前面的new MultiThreadSingleton() 还没有返回给instance,别的线程也会进if这个block,最后会构造出来多个实例
instance = new MultiThreadSingleton();
}
主程序:
class Program
{
static internal Thread[] trList = new Thread[10];
static void Run()
{
MultiThreadSingleton mt = MultiThreadSingleton.Instance;
}
static void Main( string[] args )
{
for (int i = 0; i < 10; i++)
{
trList[i] = new Thread(new ThreadStart(Run));
trList[i].Start();
}
Console.Read();
}
}
多线程Singleton模式
代码
class
SingletonMuli
{
private
static
volatile
SingletonMuli _instance;
private
static
object
lockHelper
=
new
object
();
private
SingletonMuli(){}
public
static
SingletonMuli Instance
{
get
{
lock
(lockHelper)
{
if
(_instance
==
null
)
{
_instance
=
new
SingletonMuli();
}
}
}
return
_instance;
}
}
volatile是什么意思?见volatile关键字
上面的实现,线程每个Instance 属性方法的调用中都出现独占锁定。
多线程Singleton模式优化:
使用DCL(Double-Check Locking)
使用DCL优化多线程Singleton模式
public sealed class Singleton
{
static Singleton instance = null;
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;
}
}
}
这样,线程不是每次都加锁,只有判断对象实例没有被创建时它才加锁。
.NET平台支持的Singleton模式
.NET平台下Singleton
class Singleton
{
public static readonly Singleton _instance = new Singleton(); //内联初始化
private Singleton(){}
}
内联初始化 的代码相当于:
public static readonly Singleton _instance;
static Singleton()
{
_instance = new Singleton();
}
访问静态字段的时候,静态构造函数必先被执行。即,初始化放在静态构造器;前面的单线程、多线程,放在属性中初始化。
(beforefileInit,在编译好的singleton类的metadata中可看到),实现了Lazy-initialization
同时.readonly保证只有一个实例;对NET静态构造器,编译器保证了多线程下自动加锁。
所以,NET平台下的多线程Singleton模式:
uses a private constructor and a static readonly instance variable that is lazily initialized. Thread safety is guaranteed by the compiler. (dofactory)
参数化的Singleton模式
上面的方法的缺陷是 静态构造器必须是私有的、无参的;参数化的Singleton如何实现?
1. 单线程、多线程---属性改为方法 Singleton s1 = Singleton.GetInstance(100,200);
2. 静态初始化 ---增加属性,或增加Init()方法初始化资源。Singleton s1 = Singleton.Instance(); s1.Init();
Singleton模式实例 负载均衡模型
多服务器环境,为确保任务均衡,任务分配器随机(选择最简单的策略)挑选一台服务器提供服务。
只能有一个任务分配器实例(Singleton模式!)并且任务分配器知道每个服务器的状态。
Only a single instance (the singleton) of the class can be created because servers may dynamically come on- or off-line and every request must go throught the one object that has knowledge about the state of the (web) farm.
载均衡模型
// Singleton pattern -- Real World example
using System;
using System.Collections;
using System.Threading;
// "Singleton"
class LoadBalancer
{
// Fields
private static LoadBalancer balancer;
private ArrayList servers = new ArrayList();
private Random random = new Random();
// Lock synchronization object
private static object syncLock = new object();
// Constructors (protected)
protected LoadBalancer()
{
// List of available servers
servers.Add( "ServerI" );
servers.Add( "ServerII" );
servers.Add( "ServerIII" );
servers.Add( "ServerIV" );
servers.Add( "ServerV" );
}
// Methods
public static LoadBalancer GetLoadBalancer()
{
// Support multithreaded applications through
// "Double checked locking" pattern which avoids
// locking every time the method is invoked
if( balancer == null )
{
lock(syncLock)
{
if( balancer == null )
balancer = new LoadBalancer();
}
}
return balancer;
}
// Properties
public string Server
{
get
{
// Simple, but effective random load balancer
int r = random.Next( servers.Count );
return servers[ r ].ToString();
}
}
}
/**//// <summary>
/// SingletonApp test
/// </summary>
///
public class SingletonApp
{
public static void Main( string[] args )
{
LoadBalancer b1 = LoadBalancer.GetLoadBalancer();
LoadBalancer b2 = LoadBalancer.GetLoadBalancer();
LoadBalancer b3 = LoadBalancer.GetLoadBalancer();
LoadBalancer b4 = LoadBalancer.GetLoadBalancer();
// Same instance?
if( (b1 == b2) && (b2 == b3) && (b3 == b4) )
Console.WriteLine( "Same instance" );
// All are the same instance -- use b1 arbitrarily
// Load balance 15 server requests
for (int i = 0; i < 15; i++)
{
Console.WriteLine(b1.Server);
}
// Wait for user
Console.Read();
}
}
可以看出虽然初始化没有参数,但是程序中仍然选择用GetInstance方法替代属性。
我个人认为这样可以做到概念清晰,值得提倡。
用.NET优化上面的实例
// Singleton pattern -- .NET optimized
using System;
using System.Collections;
namespace DoFactory.GangOfFour.Singleton.NETOptimized
{
// MainApp test application
class MainApp
{
static void Main()
{
LoadBalancer b1 = LoadBalancer.GetLoadBalancer();
LoadBalancer b2 = LoadBalancer.GetLoadBalancer();
LoadBalancer b3 = LoadBalancer.GetLoadBalancer();
LoadBalancer b4 = LoadBalancer.GetLoadBalancer();
// Confirm these are the same instance
if (b1 == b2 && b2 == b3 && b3 == b4)
{
Console.WriteLine("Same instance\n");
}
// All are the same instance -- use b1 arbitrarily
// Load balance 15 requests for a server
for (int i = 0; i < 15; i++)
{
Console.WriteLine(b1.Server);
}
// Wait for user
Console.Read();
}
}
// Singleton
sealed class LoadBalancer
{
// Static members are lazily initialized.
// .NET guarantees thread safety for static initialization
private static readonly LoadBalancer instance =
new LoadBalancer();
private ArrayList servers = new ArrayList();
private Random random = new Random();
// Note: constructor is private.
private LoadBalancer()
{
// List of available servers
servers.Add("ServerI");
servers.Add("ServerII");
servers.Add("ServerIII");
servers.Add("ServerIV");
servers.Add("ServerV");
}
public static LoadBalancer GetLoadBalancer()
{
return instance;
}
// Simple, but effective load balancer
public string Server
{
get
{
int r = random.Next(servers.Count);
return servers[r].ToString();
}
}
}
}
另:关于.NET下Singleton模式,
吕震宇在文章http://zhenyulu.cnblogs.com/articles/37246.html里谈到:
“利用.NET Framework平台优势实现Singleton模式,也带来了一些问题,比如无法继承,实例在程序一运行就被初始化,无法实现延迟初始化等。
详细情况可以参考微软MSDN文章:《Exploring the Singleton Design Pattern》 ”
好像和dofactory那句话不一致,现在看来,应该是dofactory说的有问题,应该是无法实现延迟初始化。
能够实现延迟初始化的方法:
延迟初始化
public sealed class Singleton
{
Singleton()
{
}
public static Singleton GetInstance()
{
return Nested.instance;
}
class Nested
{
// Explicit static constructor to tell C# compiler
// not to mark type as beforefieldinit
static Nested()
{
}
internal static readonly Singleton instance = new Singleton();
}
}