Introduction
A Singleton class is used when there must be exactly one instance of a class, and it must be accessible to all users from a well-known access point. To ensure that exactly one instance of a class is created, the class itself is made responsible for keeping track of its sole instance. Most Singleton classes ensure this by intercepting requests to create new objects. Whenever there is a request for an instance of a class, the class checks to see if an instance already exists. If the instance exists, then the class returns it, else a new instance is created, which is returned in response to the current and subsequent requests.
Need for a Per-Thread Singleton
The traditional Singleton class allows exactly one instance per process (see sidebar, How a Traditional Singleton Class Works), but in the multithreaded world of today, often you need an instance of a class for every thread in the process. For example, many Windows NT services are implemented as a server in a client-server environment. Many of these services create a separate thread to process an incoming request from a client. In most of these implementations, the different threads can run completely independent of each other. Usually each of these threads creates one or more objects, which contain the context information specific to the thread. Examples of the contents of these objects may be sockets on which the thread communicates with the client, thread-specific logging information, etc. Most of the functions that are executed as part of the thread execution need access to these objects in order to do their job. Short of passing these objects as a parameter to every function, you need a way to allow all the functions to access these objects. In a multithreaded environment, it is not possible to have this object as a global variable. A traditional Singleton class also does not work here, as you need multiple instances per process and, to be precise, a single instance per thread. This is where a per-thread Singleton class can be helpful.
Per-Thread Singleton Attributes
A thread-specific Singleton class should have the following properties:
- exactly one instance per thread
- a global point of access
- a way to destroy the thread-specific instance
The first two properties are similar to those of a traditional Singleton class. The third is usually not an issue for the traditional Singleton class since the solitary instance is created and stored as a static member variable in the class, which goes away when the program exits. However, the same strategy doesnt work for a thread-specific Singleton class. Because of the way the per-thread Singleton class is implemented, the thread-specific instance needs to be explicitly destroyed (more on this later).
Ensuring One Instance per Thread
You need a way to store instances of the class on a per thread basis. You cannot store them as static members as there is only one copy of a static member per process. The Windows NT/2000 operating system has a really cool feature called TLS (Thread Local Storage) that can help here (see the sidebar, TLS). TLS is a method by which each thread in a multithreaded process may allocate locations in which to store thread-specific data. These locations are referenced by TLS indexes, which are unique in a process. Using the same index in different threads in a process, you can retrieve data that is local to each of the threads.
Global Point of Access
Similar to the traditional Singleton classes, the per-thread Singleton classes provide a GetObject type function, which is the only publicly available interface to access an instance of the class. GetObject takes care of returning the thread-specific instance of the class to the caller.
Implementation
Listing 1 shows an implementation of a per-thread Singleton class. The implementation uses Win32 APIs for TLS. This class looks similar to the traditional Singleton class and has the following things in common:
- The constructor of the class is private.
- The class has a public, static member function called GetObject to get an instance of the class.
Although this class also has a static member variable like the traditional Singleton class, the purpose of this member is very different. This variable stores the TLS index, which can be used to retrieve the thread-specific instance of this class. Whenever you need an class object, you can call the function ThreadSingleton :: GetObject. This function checks the TLS index to see if an instance of the class already exists for the current thread. If the instance exists, this function just returns the instance; else it creates a class object, stores it in the TLS index, and returns the newly created object. Any further calls to get the object just retrieve the object from the TLS index and return that instance. This way, functions executing in different threads can all access an instance of the object created for their own thread.
As shown in Listing 1, there are two threads:
- The main thread, which gets created when the function main is entered.
- The second thread, which is created using the Win32 API CreateThread inside the function main.
Both the threads create an instance of the ThreadSingleton class using the member function GetObject. You can see that both the threads get their own copy of the ThreadSingleton object by the different thread IDs returned by the function GetThreadID.
Destroying the Per-Thread Instance
When the thread exits, the instance stored in the TLS area doesnt get deleted automatically. This instance needs to be explicitly destroyed. To destroy the instance, the class provides another static member function, ThreadSingleton :: DestroyObject. To destroy the thread-specific instance, you need to ensure that this function gets called, exactly once, at the end when the thread is exiting. The easiest way to do this is to declare an object of a class that I call ThreadSingletonDestroyer at the top of the ThreadMain function. Youll make use of the property that a thread exits whenever ThreadMain exits and that the exit of ThreadMain results in calling the destructor of the ThreadSingletonDestroyer object declared in ThreadMain. To destroy the thread-specific instance, you will call the function ThreadSingleton :: DestroyObject in the destructor of the ThreadSingletonDestroyer class, thereby ensuring that the thread-specific instance is automatically deleted when the thread exits (see Listing 1). DestroyObject is a private function of the ThreadSingleton class, preventing anybody other than the ThreadSingletonDestroyer class, which is a friend, from calling it.
Conclusion
The per-thread Singleton class provides a convenient way to access thread-specific data. No longer do you need to pass thread-specific context data to all the functions that need it; the data is just one well-known function call away. The ThreadSingleton class interface is very similar to the traditional Singleton class interface making it easy to use and understand. A benefit of the class is that it also hides operating-system-specific mechanisms to access thread-specific data. For example, many operating systems support thread-specific storage, but the usage is always different. With this class, it is easy to tailor the implementation of the class to use operating-system-specific ways to store and retrieve thread-specific data, while the interface remains the same and the caller does not need to change.
Puneesh Chaudhry is a principal software engineer for EMC Corporation in Milford, MA. He has a B.E. in Computer Science from Delhi College of Engineering. His interests include backup and other storage technologies. He can be reached at [email protected].