Singleton design pattern can be defined as to ensure a class has only one instance, and provide a global point of access to it.
In almost all application there is some requirement to have some piece of code which can be globally accessed and maintain some type of data. There are some instances in object oriented systems to have a single instance of a class or to have a predefined number of instances of a class. For example a class to maintain a global counter.
What is required is some mechanism to control how class instances are created and then ensure that only one gets created at any given time. This would give us exactly the behavior we require and free a client from having to know any class details.
Below is a sample c# code to create a singleton class.
class Singleton
{
public static Singleton Instance()
{
if (_instance == null)
{
_instance = new Singleton();
}
return _instance;
}
protected Singleton() {}
private static Singleton _instance = null;
}
The constructor is protected and that the only public method is the Instance method. In the Instance method, there is a control block (if) that checks to see if the member variable has been initialized, and if not creates a new instance. This lazy initialization in the control block means that the Singleton instance is initialized, or created, only on the first call to the Instance() method. For many applications, this approach works just fine. But, for multithreaded applications, this approach proves to have a potentially hazardous side effect.If two threads manage to enter the control block at the same time, two instances of the member variable could be created. To solve this, we use the lock keywork in C#.
Below is the Sample code using lock.
class Singleton
{
public static Singleton Instance() {
if (_instance == null) {
lock (typeof(Singleton)) {
if (_instance == null) {
_instance = new Singleton();
}
}
}
return _instance;
}
protected Singleton() {}
private static Singleton _instance = null;
}
There is still some problem in the above code. Some optimizing compilers can optimize out or reorder the lazy initialization code and reintroduce the thread safety problem. To solve this .NET framework has volatile keyword. Using the volatile keyword on the member variable declaration tells the compiler to not reorder the code and forgo optimization.
Below is the sample code using volatile keyword.
class Singleton
{
public static Singleton Instance() {
if (_instance == null) {
lock (typeof(Singleton)) {
if (_instance == null) {
_instance = new Singleton();
}
}
}
return _instance;
}
protected Singleton() {}
private static volatile Singleton _instance = null;
}
This code takes care of thread safty and compiler re-ordering issues and can be said that is the best way to implement singleton. But although the last example shows a working Singleton class using the .NET Framework and C#, this code can be greatly simplified just by taking better advantage of the .NET Framework itself. The following sample uses .NET, is a minimal Singleton class based loosely on the original GoF pattern, and still gets similar behavior.
Below is the .NET Singleton Example
sealed class Singleton
{
private Singleton() {}
public static readonly Singleton Instance = new Singleton();
}
You might ask about advantages of using lazy initialization and the hazards of multiple threads but if you try to read you can find out that all of the correct behaviors are built into the .NET Framework.
Lazy Initialization:
The Framework, during the JIT process, will initialize the static property when (and only when) any method uses this static property. If the property is not used, then the instance is not created. More precisely, what happens during JIT is that the class gets constructed and loaded when any static member of the class is used by any caller. In this case the result is the same.
Thread-safe initialization:
The Framework internally guarantees thread safety on static type initialization. In other words, in the example above, there is only one instance that would ever be created of the Singleton class.
Adding the sealed class modifier ensures that this class will not be sub-classed.
Below is a sample Singletion Usage
sealed class SingletonCounter {
public static readonly SingletonCounter Instance =
new SingletonCounter();
private long Count = 0;
private SingletonCounter() {}
public long NextValue() {
return ++Count;
}
}
class SingletonClient {
[STAThread]
static void Main() {
for (int i=0; i<20; i++) {
Console.WriteLine("Next singleton value: {0}",
SingletonCounter.Instance.NextValue());
}
}
}
I hope the above example helps you understand singleton design pattern and how c# and .NET makes it easier to implement.
Thanks
Brij
No comments:
Post a Comment