Implementing the Singleton Pattern in Visual Basic.Net
Object creation can be expensive in terms of processing time and memory utilization. While there are cases where multiple object instances are inevitable to getting things done, there are certainly cases where only a single instance of object is sufficient. For example, if your program is only going to access a single database instance, it suffice to dedicate a single object that manages database connection(s) to your database server. Other examples are objects used for logging messages, objects used for loading/saving configuration details, objects used for managing threads and objects used for managing access to shared hardware devices such as the printer.
How to make a class a Singleton?
- Declare every constructor in the class to prevent instantiation from outside of the class.
- Allocate memory to hold the single object instance, making it accessible only to fellow class members.
- Expose at least one class member to retrieve the single object instance.
One sample implementation
Public Class Singleton ' Allocate memory space to hold the ' single object instance Private Shared objSingleton As Singleton ' Make the only constructor private ' to prevent initialization outside of ' the class. Private Sub New() ' Heavy configuration works End Sub ' Expose getInstance() for the retrieval ' of the single object instance. Public Shared Function getInstance() As Singleton ' Initialize singleton through lazy ' initialization to prevent unused ' singleton from taking up program ' memory If (objSingleton Is Nothing) Then objSingleton = New Singleton() End If Return objSingleton End Function End Class
The above sample will work as intended in single-threaded programs. However, in programs where the getInstance() method is accessed by multiple threads, more than one instances may be created and held by different threads. For instance, suppose Thread A and Thread B are accessing the getInstance() method at the same time. Thread A gets to run and is preempted right after it had checked that objSingleton is not yet initialized. Thread B runs and also checked that objSingleton is not yet initialized. When Thread A and Thread B complete the call to the getInstance() method, two instances of Singleton will be held by Thread A and Thread B concurrently.
Another implementation for multi-threaded programs
Imports System.Threading Public Class Singleton ' For SyncLock to mark a critical section Private Shared classLocker As New Object() ' Allocate memory space to hold the ' single object instance Private Shared objSingleton As Singleton ' Make the only constructor private ' to prevent initialization outside of ' the class. Private Sub New() ' Heavy configuration works End Sub ' Expose getInstance() for the retrieval ' of the single object instance. Public Shared Function getInstance() As Singleton ' Initialize singleton through lazy ' initialization to prevent unused ' singleton from taking up program ' memory If (objSingleton Is Nothing) Then ' Mark a critical section where ' threads take turns to execute SyncLock (classLocker) If (objSingleton Is Nothing) Then objSingleton = New Singleton() End If End SyncLock End If Return objSingleton End Function End Class
The additional classLocker object is for the SyncLock construct to mark a critical section where Threads take turns to execute the coding. As SyncLock is expensive, we don't want every attempt to retrieve the singleton object to go through SyncLock, which is why we mark the critical section only when the singleton instance is not yet initialized. The same check inside the SyncLock block is to make sure that ultimately, only one thread will initialize the singleton object. For instance, in the Thread A and Thread B scenario mentioned earlier, both threads will queue up to execute the SyncLock block. Without the second check, both threads will initialize the singleton object when they get their turn to execute the critical section.
2 Comments
Thanks for this post. Just wanted to point out that your private constructors should not be Shared – you might want to fix that.
Hi Daniel,
Thanks for dropping by with your suggestion. I had updated the codes with your suggestion.