- Singleton Mechanics
- Singletons and Threads
- Recognizing Singleton
- Summary
Singletons and Threads
To lazy-initialize a singleton in a multi-threaded environment, you must prevent multiple threads from initializing the singleton.
In a multi-threaded environment, there is no guarantee that a method will run to completion before a method in another thread starts running. This opens the possibility, for example, that two threads will try to initialize a singleton at roughly the same time. Suppose that a method finds that a singleton is null. If another thread begins executing at that moment, it will also find that the singleton is null. Then both methods will proceed to initialize the singleton. To prevent this sort of contention, you need a locking facility to help coordinate methods running in different threads.
The C# language and .NET FCL include good support for multi-threaded development. In particular, C# supplies every object with a lock, an exclusive resource that represents possession of the object by a thread. To ensure that only one thread initializes a singleton, you can synchronize the initialization on the lock of a suitable object. Other methods that require exclusive access to the singleton can synchronize on the same lock. For advice on concurrent OO programming, Concurrent Programming in Java [Lea] is an excellent resource. This book suggests synchronizing on the lock that belongs to the class itself, as in the following code:
using System; namespace BusinessCore { public class Factory { private static Factory _factory; private static Object _classLock = typeof(Factory); private long _wipMoves; private Factory() { _wipMoves = 0; } public static Factory GetFactory() { lock (_classLock) { if (_factory == null) { _factory = new Factory(); } return _factory; } } public void RecordWipMove() { // challenge! } } }
The GetFactory() code ensures that if a second thread begins to lazy-initialize the singleton after another thread has begun the same initialization, the second thread will wait to obtain the _classLock object’s lock. When it obtains the lock, the second thread will find that the singleton is no longer null.
The _wipMoves variable records the number of times that work in process (WIP) advances. Every time a bin moves onto a new machine, the subsystem that causes or records the move must call the factory singleton’s RecordWipMove() method.
Challenge 8.3
Write the code for the RecordWipMove() method of the Factory class.
A solution appears on page 363.