Whenever we have a scenario in our application where
- A single instance of an class is required in the application and the class itself is able to enforce the single instance on itself. The rest of the system need not have to worry about managing the single instance.
- This single instance class should be accessible by the complete system or say by most part of the system.
- This single instance subsystem should not be created and initialized unless it is required. (lazy initialization)
Now let us see how we can have a class that will let the callers create only one single instance of it. To do that the first thing we should ask ourselves is how instance of the classes are created. The answer to this question is by calling new for the class which in turn will call the constructor of the class. So if we want the applications not to be able to create by number of instances, we should first restrict the access to the constructor i.e. make the constructor private.
Now if we are making the constructor private, next question we have is how will we be creating the instance of this class. Since the private constructor can still be called from the class itself, we can create an instance of this class inside the class itself. So to do this we need to create a static method(static method will not need callers to have an instance to be called) inside the class i.e.GetInstancewhich will create an object of this class and return it to the applications.
Using the Code
let us now look at the class that is implementing the instance creation using the above discussed method and returning the instance to the caller.
Now the above class will let the user create only one instance of this class i.e. singleton in the simplest of forms.
Hello Multi-Threading
There is a problem with this class. it is not thread safe. So to make it thread safe, we need to make sure that the instantiation code can only be accessed by only a single thread at any given time. So lets do that and introduce a lock in our class to guard the GetInstancecalls.
Now the instance retrieval code can only be accessed by one single thread at any given time. But this incorporates a performance problem. Our idea was to not let multiple threads be able to create multiple instances so we needed to guard the instance creation part in lock. What we have done instead is that we have guarded the complete method with lock. Which would mean that the lock will be acquired even when the instance has been created and it just needs to be returned. So to circumvent this problem, we need to guard only the instance creation part under lock and not the instance return part.
Now what we have is a class that will acquire lock only for first instance creation and rest of the time it will return the already created instance.
Using the .NET CLR capabilities for Thread safety
When we are using C#, we can utilize the CLR behaviour to have a thread safe implementation without the complexity of locking. What we could do is that we could include a static constructorin the class that will take care of the instance creation. The static constructor of a class is called when either the first static member of the class is accessed(in our case it will beGetInstancemethod) or the first instance of the class is created(which is not a valid scenario here as constructor is private). So lets look at the implementation with the following changes.
So what we have done here is that we have moved the instantiation in the static constructor which will get called when the GetInstance method will be called. Static constructor will create the instance and that instance will get returned from by the GetInstance method.
The instance field has been marked as readonly so that it can only be instantiated during static initialization. And now we have a fairly usable singleton class that is thread safe without the performance overhead of locks.
Note: One might argue that the in our last version, the initialization is not lazy as if this class has any other static member then the instance will get created even if it is not requested. There is one more version of singleton possible where this can be circumvented using either nested class or Lazy<T>. But I am not going to discuss them here. In most cases a singleton will not have other static methods. If we find ourselves in a scenario where we have a singleton that also has other static methods, perhaps we are violating Single Responsibility Principle and should revisit our class design again.
No comments:
Post a Comment