引子

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

模型图

单线程Singleton模式

使用statics约束构造函数,在公有属性中实现类的实例化。

ContractedBlock.gif ExpandedBlockStart.gif 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),就可能最后出来两个实例。

ContractedBlock.gif ExpandedBlockStart.gif 简单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模式

ExpandedBlockStart.gif 代码
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)
ContractedBlock.gif ExpandedBlockStart.gif 使用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模式

ContractedBlock.gif ExpandedBlockStart.gif .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.

ContractedBlock.gif ExpandedBlockStart.gif 载均衡模型
// 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方法替代属性。
我个人认为这样可以做到概念清晰,值得提倡。

ContractedBlock.gif ExpandedBlockStart.gif 用.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说的有问题,应该是无法实现延迟初始化。

能够实现延迟初始化的方法:

ContractedBlock.gif ExpandedBlockStart.gif 延迟初始化
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();
  }
}

 

转载于:https://www.cnblogs.com/DylanWind/archive/2008/11/10/1328959.html

更多推荐