DucVA's Blog

Singleton Pattern | June 28, 2010

Problem:
Trong nhiều trường hợp, yêu cầu cần phải kiểm soát được quá trình tạo ra đối tượng và kiểm soát vòng đời của đối tượng, trong khi đối tượng này được rất nhiều đối tượng khác của hệ thống gọi đến. Trường hợp thường gặp nhất là cần đảm bảo tại mọi thời điểm chỉ có duy nhất một thể hiện của 1 đối tượng tồn tại.

Solution: Singleton pattern

Singleton pattern giái quyết vấn đề kiểm soát quá trình tạo ra thể hiện của đối tượng bằng cách ẩn đi phương thức khởi tạo của đối tượng. Đồng thời cung cấp 1 phương thức khác để cung cấp thể hiện của chính mình cho các đối tượng khác.

Có một số cách để cài đặt Pattern này như sau:

First,

public sealed class Singleton
{
static Singleton instance=null;

Singleton()
{
}

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

Đây là cách đơn giản nhất, nhưng gặp phải 2 vấn đề:
– Non thread-safe: Nếu có 2 thread t1 và t2 cùng gọi đến signleton cùng 1 lúc, và cùng kiểm tra điểu kiện instance==null 1 lúc, thì dẫn đến cả 2 thread này cùng gọi lệnh khởi tạo của Singleton() cùng 1 lúc. Điều này phá vỡ mục đích của Singleton Pattern.
– multi-check: mỗi lần gọi Instance của Singleton, hệ thống phải kiểm tra điều kiện instance==null 1 lần.

Vì 2 vấn đề này, cách này không được sử dụng.

Second,


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;
}
}
}
}

Cách này cải tiến hơn cách 1, đó là sử dụng 1 biến tránh hiện tượng đụng độ giữa 2 thread. Trường hợp nhiều thread cùng gọi đến Singleton cùng 1 lúc, đến dòng lệnh lock(padlock) thì chỉ có 1 thread được phép tiếp tục, các thread khác phải chờ đến khi thread này thực hiện xong và giải phóng biến padlock. Nhờ vậy sẽ không xảy ra hiện tượng 2 thread cùng khởi tạo Singleton.
Cách này cũng có một số nhược điểm:
– Sử dụng biến padlock sẽ tốn thêm bộ nhớ cho Singleton.
– Sử dụng lock đòi hỏi thêm thời gian xử lý.

Third,


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;
}
}
}

Cách này tránh hiện tượng đụng độ giữa 2 thread bằng cách khởi tạo Instance ngay từ đầu, do đó cũng sẽ không cần phải kiểm tra điều kiện nào nữa.
Cách này có nhược điểm là phải khởi tạo Instance ngay từ đầu, cho dù có thể sau này không sử dụng đến.

Fourth,


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();
}
}

Đây là cách tối ưu nhất so với những cách còn lại. Bằng cách đưa thể hiện Instance vào trong nested class, sẽ tránh được việc phải khởi tạo Instance ngay từ đầu. Chỉ khi nào Nested class được đụng tới thì instance mới được khởi tạo.
Thêm vào đó không cần phải kiểm tra bất kỳ điều kiện nào.


Leave a Comment »

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

%d bloggers like this: