The following implementation allows only a single thread to enter the critical area, which the lock block identifies, when no instance of Singleton has yet been created. This approach ensures that only one instance is created and only when the instance is needed. Also, the variable is declared to be volatile to ensure that assignment to the instance variable completes before the instance variable can be accessed. Lastly, this approach uses a syncRoot instance to lock on, rather than locking on the type itself, to avoid deadlocks.

using System;

/// <summary>
/// Multi-threaded Singleton Class
///
/// The following implementation allows only a single thread to enter the
/// critical area, which the lock block identifies, when no instance of
/// Singleton has yet been created.
///
/// This approach ensures that only one instance is created and only when the
/// instance is needed. Also, the variable is declared to be volatile to
/// ensure that assignment to the instance variable completes before the
/// instance variable can be accessed. Lastly, this approach uses a syncRoot
/// instance to lock on, rather than locking on the type itself, to avoid
/// deadlocks.
///
/// https://msdn.microsoft.com/en-us/library/ff650316.aspx
/// </summary>
public sealed class Singleton
{
    private static volatile Singleton _instance;
    private static object _syncRoot = new Object();

    private Singleton() {}

    public static Singleton Instance
    {
        get
        {
            if (_instance == null)
            {
                lock (_syncRoot)
                {
                    if (_instance == null)
                    {
                        _instance = new Singleton();
                    }
                }
            }
            return _instance;
        }
    }
}