Below is some code from a C# book to show how Singleton pattern is constructed in multithreading:
internal sealed class Singleton {
// s_lock is required for thread safety and having this object assumes that creating
// the singleton object is more expensive than creating a System.Object object
private static readonly Object s_lock = new Object();
// This field will refer to the one Singleton object
private static Singleton s_value = null;
// Private constructor prevents any code outside this class from creating an instance
private Singleton() {
// Code to initialize the one Singleton object goes here...
}
// Public, static method that returns the Singleton object (creating it if necessary)
public static Singleton GetSingleton() {
// If the Singleton was already created, just return it (this is fast)
if (s_value != null) return s_value;
Monitor.Enter(s_lock); // Not created, let 1 thread create it
if (s_value == null) {
// Still not created, create it
Singleton temp = new Singleton();
// Save the reference in s_value (see discussion for details)
Volatile.Write(ref s_value, temp);
}
Monitor.Exit(s_lock);
// Return a reference to the one Singleton object
return s_value;
}
}
I get the idea why the code does:
Singleton temp = new Singleton();
Volatile.Write(ref s_value, temp);
instead of
s_value = new Singleton();
because the compiler can allocate memory for the Singleton
, assign the reference into s_value
, and then call the constructor. From a single thread's perspective, changing the order like this has no impact. But if after publishing the reference into s_value
and before calling the constructor, another thread calls the GetSingleton
method, then thread will see that s_value
is not null and start to use the Singleton
object, but its constructor has not finished executing yet.
But I don't understand why we have to use Volatile.Write
, can't we do:
Singleton temp = new Singleton();
s_value = temp;
The compiler cannot reorder e.g execute s_value = temp
first then execute Singleton temp = new Singleton()
, because temp
have to exist before s_value = temp
?