Thread safe Singleton: why the memory model does not guarantee that the new instance will be seen by other threads?

I have read in Jon's Skeet online page about how to create a thread safe Singleton in C#

http://csharpindepth.com/Articles/General/Singleton.aspx

// Bad code! Do not use!
public sealed class Singleton
{
    private static Singleton instance=null;

    private Singleton()
    {
    }

    public static Singleton Instance
    {
        get
        {
            if (instance==null)
            {
                instance = new Singleton();
            }
            return instance;
        }
    }
}

in the paragraph below this code it says:

As hinted at before, the above is not thread-safe. Two different threads could both have evaluated the test if (instance==null) and found it to be true, then both create instances, which violates the singleton pattern. Note that in fact the instance may already have been created before the expression is evaluated, but the memory model doesn't guarantee that the new value of instance will be seen by other threads unless suitable memory barriers have been passed.

Can you please explain why doesn't the memory model does not guarantee that the new value of instance will be seen by other threads?

the static variable is located on the heap, but why it is not shared with other threads immediately? do we need to wait for the context switch so the other thread will know the instance is not null anymore?

Jon Skeet
people
quotationmark

Can you please explain why doesn't the memory model does not guarantee that the new value of instance will be seen by other threads?

The memory model is complex and not terribly clearly documented at the moment, but fundamentally there are very few situations where it's safe to rely on a value that's written by one thread being "seen" on another thread without either some locking or other inter-thread communication going on.

For example, consider this:

// Bad code, do not use
public class BigLoop
{
    private static bool keepRunning = true;

    public void TightLoop()
    {
        while (keepRunning)
        {
        }
    }

    public void Stop()
    {
        keepRunning = false;
    }
}

If you created two threads, one of which calls TightLoop and another of which calls Stop, there's no guarantee that the looping method will ever terminate.

There are lots of levels of caching in modern CPUs, and requiring that every read goes back to main memory would remove a lot of optimizations. So we have memory models which make guarantees about which changes will definitely be visible in what situations. Other than those guarantees, the JIT compiler is allowed to assume that there's effectively only a single thread - so it could cache the value of the field in a register, and never hit main memory again, for example.

The currently-documented memory model is woefully inadequate, and would suggest that some clearly-weird optimizations should be valid. I wouldn't go too far down that route, but it's worth reading Joe Duffy's blog post on the CLR 2.0 memory model. (That's stronger than the documented ECMA memory model, but a blog post isn't the ideal place for such a key piece of documentation, and I think more clarity is still needed.)

The static variable is located on the heap, but why it is not shared with other threads?

It is shared with other threads - but the value won't necessarily immediately be visible.

people

See more on this question at Stackoverflow