Wednesday, May 21, 2008

Thread safe Singleton pattern

关于Singleton模式的几个误区
网上有很多Singleton模式的样板, 多数是错误的, 下面的文档提出了很好的样板.
http://msdn.microsoft.com/en-us/library/ms998558.aspx
http://www.devhood.com/tutorials/tutorial_details.aspx?tutorial_id=486
http://www.yoda.arachsys.com/csharp/singleton.html
http://codebetter.com/blogs/steve.hebert/archive/2005/12/16/135697.aspx
另外特别说明的是:
1. Singleton class的Instance属性或者公开方法GetInstance()不应带参数, 也就是说无论在什么情况下获取Singleton类的对象, 都始终是那一个, 不会有不同的实例.
2. Singleton类不能是Static类, 因为Static类不能包含任何实例方法(包括实例constructor). 但Singleton类一定要包含一个static变量(比如_Instance, 其类型为Singleton)来存储Singleton类的实例, 那为什么必须是static变量呢? 因为我们在使用Singleton类的时候, 不是通过constructor类创建一个Singleton对象, 而是需要通过调用一个static方法(比如GetInstance())来创建一个实例, 而static方法只能使用static变量, 不能使用任何非static变量, 这就要求存放Singleton实例的内部变量必须是static变量.
3. Singleton类必须要使用lock, 否则就不是线程安全的, 除非_Instance这个static变量, 同时被声明为readonly.
4. 一般情况下, 不要Singleton模式, 不必使用Double check+Lock这样的代码, 因为写法复杂, 而且容易会造成线程不安全. 尤其是在多核机器上. 可以直接使用Single Check+Lock写法, 简单而又安全.
Bad Code
Good Code
// Bad code! Do not use! Not Thread safe, 
public sealed class Singleton
{
static Singleton instance=null;
Singleton()
{
}
public static Singleton Instance
{
get
{
if (instance==null)
{
instance = new Singleton();
}
return instance;
}
}
}
//Thread safe, 不是Lazy load, 特点:实现简单
//But performance suffers as a lock is acquired every time the instance is requested
public sealed class Singleton
{
static Singleton instance = null;
static readonly object padlock = new object();

Singleton()
{
}

public static Singleton Instance
{
get
{
lock (padlock)
{
if (instance == null)
{
instance = new Singleton();
}
return instance;
}
}
}
}


// Try to double Check, But still is not thread safe! Do not use!
//Thread safe issue see:
//
http://www.cs.umd.edu/~pugh/java/memoryModel/DoubleCheckedLocking.html
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;
}
}
}
//Thread safe,虽然不是Lazy load. 它使用了static Initilization,特点:简单
public sealed class Singleton
{
private static readonly Singleton instance = new Singleton();

private Singleton(){}

public static Singleton Instance
{
get
{
return instance;
}
}
}
// double Check, Thread not safe
//Thread safe issue see:
//
http://www.cs.umd.edu/~pugh/java/memoryModel/DoubleCheckedLocking.html

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)
{
//线程不安全,因为编译器会进行代码优化,
//临时变量tempInstance会被舍弃,这样就编程了线程不安全代码了
Singleton tempInstance=new Singleton();
instance = tempInstance;

}
}
}
return instance;
}
}
}
// double Check, Thread safe
public sealed class Singleton
{
volatile static Singleton instance=null;
static readonly object padlock = new object();

Singleton()
{
}

public static Singleton Instance
{
get
{
if (instance==null)
{
lock (padlock)
{
//线程安全,因为instance被标记为volatile
if (instance==null)
{
Instance=new Singleton();
}
}
}
return instance;
}
}
}

thread-safe, not quite as lazy, without using locks
thread-safe, fully lazy instantiation
public sealed class Singleton
{
static readonly Singleton instance=new Singleton();

// Explicit static constructor to tell C# compiler
// not to mark type as beforefieldinit
static Singleton()
{
}

Singleton()
{
}

public static Singleton Instance
{
get
{
return instance;
}
}
}
public sealed class Singleton
{
Singleton()
{
}

public static Singleton Instance
{
get
{
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();
}
}

No comments: