2

Since I create the readonly static instance as soon as someone uses the class, no lazy loading, this code is thread safe and I do not need to follow the Double-checked locking design pattern, correct?

public class BusSingleton<T> where T : IEmpireEndpointConfig, new()
{

    private static readonly BusSingleton<T> instance = new BusSingleton<T>();
    private IBus bus;

    public IBus Bus
    {
        get { return this.bus; }
    }

    public static BusSingleton<T> Instance
    {
        get
        {
            return instance;
        }
    }

    private BusSingleton()
    {
        T config = new T();
        bus = NServiceBus.Bus.Create(config.CreateConfiguration());
        ((IStartableBus) bus).Start();
    }
}
Brian Ogden
  • 18,439
  • 10
  • 97
  • 176
  • Possible duplicate of [Thread Safe C# Singleton Pattern](http://stackoverflow.com/questions/12316406/thread-safe-c-sharp-singleton-pattern) – Matt Hensley Jan 18 '17 at 20:56
  • I do not agree the duplicate suggestions. The duplicate just shows how to do double checked locking. This is asking "is double checked locking needed in this specific situation?" – Scott Chamberlain Jan 18 '17 at 20:58

2 Answers2

2

During the static initializer the run-time puts a lock around the object's type so two instances of the initializer can not be run at the same time.

The only thing you must be careful of is if NServiceBus.Bus.Create, config.CreateConfiguration, or bus.Start() use multiple threads internally and try to access your object's type anywhere within the class/function on that other thread you could deadlock yourself if one of those three function calls does not return until after that internal thread is done.

When you do the traditional "lazy singleton" with double checked locking the static initializer will have already finished and you don't run the risk of deadlocking yourself.

So if you are confidant that those 3 functions will not try to access your type on another thread then it is fine to not use double checked locking for your use case.

Scott Chamberlain
  • 124,994
  • 33
  • 282
  • 431
  • Does the static initializer lock work for all values of `T`, or just each? That is, if thread A does `BusSingleton` and thread B does `BusSingleton` - can the type initializer for `BusSingleton` run in parallel for each type of T, `Bar` and `Foo`? *If* that's how it works, then the instance constructors will run parallel, and thus `Create` and `Start` need to be thread safe, too. – vcsjones Jan 18 '17 at 20:57
  • Yes they can be run in parallel, when working with statics and generics just in your mind replace `<` and `>` with `_`. Now ask your question again *"if thread at does `BusSingleton_Foo_` and another thread does `BusSingleton_Bar_` - can the type initializers run in parallel for Bar and Foo?"* The answer to that is "Of course they can, they are two different types" – Scott Chamberlain Jan 18 '17 at 21:01
  • @vcsjones I do agree that `Create` must be thread safe, but assuming `Create` creates a new instance of the `IStartableBus` object then `Start` does not need to be thread safe because it is local to the function. – Scott Chamberlain Jan 18 '17 at 21:02
  • agree - but I don't know what NServiceBus does. For all I know `Start` accesses / uses some shared static in it's implementation (hypothetically). So the safety of this depends entirely on NServiceBus's API. – vcsjones Jan 18 '17 at 21:03
0

That looks safe as long as you don't need to delay the instantiation to run initalization code or anything like that. Which it sounds like you don't need. https://msdn.microsoft.com/en-us/library/ff650316.aspx

skalpin
  • 310
  • 1
  • 10