8

Isn't this a simpler as well as safe (and hence better) way to implement a singleton instead of doing double-checked locking mambo-jambo? Any drawbacks of this approach?


public class Singleton
{
    private static Singleton _instance;
    private Singleton() { Console.WriteLine("Instance created"); }

    public static Singleton Instance
    {
        get
        {
            if (_instance == null)
            {
                Interlocked.CompareExchange(ref _instance, new Singleton(), null);
            }
            return _instance;
        }
    }
    public void DoStuff() { }
}

EDIT: the test for thread-safety failed, can anyone explain why? How come Interlocked.CompareExchange isn't truly atomic?


public class Program
{
   static void Main(string[] args)
   {
      Parallel.For(0, 1000000, delegate(int i) { Singleton.Instance.DoStuff(); });
   }
} 

Result (4 cores, 4 logical processors)
Instance created
Instance created
Instance created
Instance created
Instance created
kateroh
  • 4,382
  • 6
  • 43
  • 62
  • 3
    Wouldn't this example still cause a problem if two threads were able to get inside of the `if (_instance == null)` check before either of them executed the `Interlocked.CompareExchange` function? – Tim May 13 '11 at 19:23
  • 1
    That is not true. According to MSDN doc: "The compare and exchange operations are performed as an atomic operation." – kateroh May 13 '11 at 19:33
  • @kateroh - Ah, i see, I glossed voer it being a compare and exchange at the same time. so its a double-check. My bad. – Tim May 13 '11 at 19:39
  • 1
    @kateroh, but the `if` check is **not** atomic. Neither is the `new`. – Blindy May 13 '11 at 19:43
  • The new Singleton gets called before the call to CompareExchange actually occurs. So it's getting created either way. I'm guessing you'd find that its only overwriting it once, though. – Tim May 13 '11 at 19:46
  • @Blindy: it is ok if `if` is not atomic. If CompareExchange would be truly atomic, then the second time it is called `ref _instance` will not be null and it should not be assigned `new Singleton()` – kateroh May 13 '11 at 19:46
  • 1
    But you're still creating it. How are you not seeing the problem? – Blindy May 13 '11 at 19:48
  • I just refreshed my memory regarding Interlocked.CompareExchange, now I understand how it works and why it fails. Thanks! – kateroh May 13 '11 at 20:00
  • It gets instantiated multiple times, but the compare and set if null (CompareExchange) is atomic, and thus only gets called once. The other created instances will get GC'd – ChrisMcJava Jun 08 '17 at 19:56

13 Answers13

10

If your singleton is ever in danger of initializing itself multiple times, you have a lot worse problems. Why not just use:

public class Singleton
{
  private static Singleton instance=new Singleton();
  private Singleton() {}

  public static Singleton Instance{get{return instance;}}
}

Absolutely thread-safe in regards to initialization.

Edit: in case I wasn't clear, your code is horribly wrong. Both the if check and the new are not thread-safe! You need to use a proper singleton class.

Blindy
  • 65,249
  • 10
  • 91
  • 131
  • 1
    This doesn't give the same features as what the posters code does though. The OP's code only returns _instance when it is referenced (and so only initializes it if used) whereas yours initializes it regardless if the class is needed or not. – Streklin May 13 '11 at 19:26
  • Hmm fair enough - I'll not be on it. But would you mind explaining what I missed? I don't see how the OP's code gets initialized unless the get function is called on the Instance property. – Streklin May 13 '11 at 19:30
  • 1
    @Blindy - thanks! Although it appears you passed up an excellent chance to take me for my money ;) – Streklin May 13 '11 at 19:35
  • +1 for Streklin. the code needs to init the instance on demand, which is a very reasonable ask from any singleton implementation. – kateroh May 13 '11 at 20:06
  • @kateroh - the link provided by Blindy states that static fields are, by default, not initialized until first referenced. That means that a static field with initializer as stated in the answer will behave lazily. – KeithS May 13 '11 at 20:23
  • 1
    @Blindy, @KeithS: "The static field initializers are **executed at an implementation-dependent time** prior to the first use of a static field of that class." - All this says is that it is *definitely* initialised before you use the class for the first time, but does not state *when*. It is **not** lazy loaded, it is undetermined and depends on the CLR you happen to use. This is an extra reason [`Lazy`](http://msdn.microsoft.com/en-us/library/dd642331.aspx) exists in the framework. – Anastasiosyal Feb 16 '12 at 16:46
8

You may well be creating multiple instances, but these will get garbage collected because they are not used anywhere. In no case does the static _instance field variable change its value more than once, the single time that it goes from null to a valid value. Hence consumers of this code will only ever see the same instance, despite the fact that multiple instances have been created.

Lock free programming

Joe Duffy, in his book entitled Concurrent Programming on Windows actually analyses this very pattern that you are trying to use on chapter 10, Memory models and Lock Freedom, page 526.

He refers to this pattern as a Lazy initialization of a relaxed reference:

public class LazyInitRelaxedRef<T> where T : class
{
    private volatile T m_value;
    private Func<T> m_factory;

    public LazyInitRelaxedRef(Func<T> factory) { m_factory = factory; }


    public T Value
    {
        get
        {
            if (m_value == null) 
              Interlocked.CompareExchange(ref m_value, m_factory(), null);
            return m_value;
        }
    }

    /// <summary>
    /// An alternative version of the above Value accessor that disposes
    /// of garbage if it loses the race to publish a new value.  (Page 527.)
    /// </summary>
    public T ValueWithDisposalOfGarbage
    {
        get
        {
            if (m_value == null)
            {
                T obj = m_factory();
                if (Interlocked.CompareExchange(ref m_value, obj, null) != null && obj is IDisposable)
                    ((IDisposable)obj).Dispose();
            }
            return m_value;
        }
    }
}

As we can see, in the above sample methods are lock free at the price of creating throw-away objects. In any case the Value property will not change for consumers of such an API.

Balancing Trade-offs

Lock Freedom comes at a price and is a matter of choosing your trade-offs carefully. In this case the price of lock freedom is that you have to create instances of objects that you are not going to use. This may be an acceptable price to pay since you know that by being lock free, there is a lower risk of deadlocks and also thread contention.

In this particular instance however, the semantics of a singleton are in essence to Create a single instance of an object, so I would much rather opt for Lazy<T> as @Centro has quoted in his answer.

Nevertheless, it still begs the question, when should we use Interlocked.CompareExchange? I liked your example, it is quite thought provoking and many people are very quick to diss it as wrong when it is not horribly wrong as @Blindy quotes.

It all boils down to whether you have calculated the tradeoffs and decided:

  • How important is it that you produce one and only one instance?
  • How important is it to be lock free?

As long as you are aware of the trade-offs and make it a conscious decision to create new objects for the benefit of being lock free, then your example could also be an acceptable answer.

Anastasiosyal
  • 6,494
  • 6
  • 34
  • 40
6

In order not to use 'double-checked locking mambo-jambo' or simply not to implement an own singleton reinventing the wheel, use a ready solution included into .NET 4.0 - Lazy<T>.

Centro
  • 3,892
  • 2
  • 25
  • 31
3
public class Singleton
{
    private static Singleton _instance = new Singleton();
    private Singleton() {}

    public static Singleton Instance
    {
        get
        {
            return _instance;
        }
    }
}
Oded
  • 489,969
  • 99
  • 883
  • 1,009
  • Where is a static constructor ? – ZOXEXIVO Aug 09 '16 at 23:04
  • @ZOX - not needed. – Oded Aug 10 '16 at 08:25
  • If no static constructor - your singleton may be createad any time at start and not in time when it really need – ZOXEXIVO Aug 10 '16 at 16:48
  • @ZOX - a static constructor has the same issue. – Oded Aug 10 '16 at 17:47
  • _The laziness of type initializers is only guaranteed by .NET when the type isn't marked with a special flag called beforefieldinit. Unfortunately, the C# compiler (as provided in the .NET 1.1 runtime, at least) marks all types which don't have a static constructor._ From [here](http://csharpindepth.com/Articles/General/Singleton.aspx) – Pedro Perez Mar 05 '18 at 12:09
3

I am not convinced you can completely trust that. Yes, Interlocked.CompareExchanger is atomic, but new Singleton() is in not going to be atomic in any non-trivial case. Since it would have to evaluated before exchanging values, this would not be a thread-safe implementation in general.

Streklin
  • 849
  • 1
  • 10
  • 19
3

what about this?

public sealed class Singleton
{
    Singleton()
    {
    }

    public static Singleton Instance
    {
        get
        {
            return Nested.instance;
        }
    }

    class Nested
    {
        // Explicit static constructor to tell C# compiler
        // not to mark type as beforefieldinit
        static Nested()
        {
        }

        internal static readonly Singleton instance = new Singleton();
    }
}

It's the fifth version on this page: http://www.yoda.arachsys.com/csharp/singleton.html

I'm not sure, but the author seems to think its both thread-safe and lazy loading.

Tim
  • 14,999
  • 1
  • 45
  • 68
2

Your singleton initializer is behaving exactly as it should. See Raymond Chen's Lock-free algorithms: The singleton constructor:

This is a double-check lock, but without the locking. Instead of taking lock when doing the initial construction, we just let it be a free-for-all over who gets to create the object. If five threads all reach this code at the same time, sure, let's create five objects. After everybody creates what they think is the winning object, they called Interlocked­Compare­Exchange­Pointer­Release to attempt to update the global pointer.

This technique is suitable when it's okay to let multiple threads try to create the singleton (and have all the losers destroy their copy). If creating the singleton is expensive or has unwanted side-effects, then you don't want to use the free-for-all algorithm.

Each thread creates the object; as it thinks nobody has created it yet. But then during the InterlockedCompareExchange, only one thread will really be able to set the global singleton.

Bonus reading

  • One-Time Initialization helper functions save you from having to write all this code yourself. They deal with all the synchronization and memory barrier issues, and support both the one-person-gets-to-initialize and the free-for-all-initialization models.
  • A lazy initialization primitive for .NET provides a C# version of the same.
Ian Boyd
  • 246,734
  • 253
  • 869
  • 1,219
1

Auto-Property initialization (C# 6.0) does not seem to cause the multiple instantiations of Singleton you are seeing.

public class Singleton
{    
    static public Singleton Instance { get; } = new Singleton();
    private Singleton();
}
1

This is not thread-safe.

You would need a lock to hold the if() and the Interlocked.CompareExchange() together, and then you wouldn't need the CompareExchange anymore.

H H
  • 263,252
  • 30
  • 330
  • 514
1

You still have the issue that you're quite possibly creating and throwing away instances of your singleton. When you execute Interlocked.CompareExchange(), the Singleton constructor will always be executed, regardless of whether the assignment will succeed. So you're no better off (or worse off, IMHO) than if you said:

if ( _instance == null )
{
  lock(latch)
  {
    _instance = new Singleton() ;
  }
}

Better performance vis-a-vis thread contention than if you swapped the position of the lock and the test for null, but at the risk of an extra instance being constructed.

Nicholas Carey
  • 71,308
  • 16
  • 93
  • 135
  • see the test in the edit, the instance is created only when `_instance == null` – kateroh May 13 '11 at 20:02
  • 1
    more than 1 thread can pass the test for null prior to `_instance` getting set. You could have, say, 4 threads stack up on the compare-and-swap. One will succeed, but 4 instances of your singleton will get created and 3 will [eventually] get thrown away. And...you've got no control over **which** thread will actually instantiate the singleton. First-in/first-out is not guaranteed when it comes to context swaps. Welcome to the fuzzy multi-threading/multi-processing world. – Nicholas Carey May 13 '11 at 20:19
1

An obvious singleton implementation for .NET?

Community
  • 1
  • 1
expelledboy
  • 2,033
  • 18
  • 18
  • Centro already mentioned a possible implementation using Lazy. Thanks anyway! – kateroh May 16 '11 at 17:50
  • Sorry I must have missed it. I actually use something abit more complicated to _ensure_ that its a singleton for use as a software license, and Lazy<> fits in perfectly! But its wasnt relevant to the question. – expelledboy May 17 '11 at 09:38
0

I think the simplest way after .NET 4.0 is using System.Lazy<T>:

public class Singleton
{
    private static readonly Lazy<Singleton> lazy = new Lazy<Singleton>(() => new Singleton());

    public static Singleton Instance { get { return lazy.Value; } }

    private Singleton() { }
}

Jon Skeet has a nice article here that covers a lot of ways of implementing singleton and the problems of each one.

fabriciorissetto
  • 9,475
  • 5
  • 65
  • 73
  • That's the [same answer](http://stackoverflow.com/a/5997760/1838048) that Centro already gave. – Oliver Oct 30 '15 at 13:52
0

Don't use locking. Use your language environment

Mostly simple Thread-safe implementation is:

public class Singleton
{
    private static readonly Singleton _instance;

    private Singleton() { }

    static Singleton()
    {
        _instance = new Singleton();
    }

    public static Singleton Instance
    {
        get { return _instance; }
    }
}
ZOXEXIVO
  • 920
  • 9
  • 21