суббота, 17 февраля 2018 г.

Потокобезопасный Singleton на C#

Написать синглтон, с которым можно быстро и безопасно работать из разных потоков, задача не такая уж и тривиальная, как кажется на первый взгляд. С блокировками это делается довольно просто, например, так:

public class Singleton
{
    private static readonly object syncObj = new object();
    private static volatile Singleton instance;

    public Singleton Shared()
    {
        if (instance == null)
        {
            lock (syncObj)
            {
                if (instance == null)
                {
                    instance = new Singleton();
                }
            }
        }

        return instance;
    }

    private Singleton()
    {            
    }
    
    // ...
}

Двойная проверка на null нужна для того, чтобы не делать блокировок без необходимости. А если хочется совсем без блокировок? Изучая исходный код пула массивов от майкрософт, нашел реализацию lockfree синглтона. Вот она, очищенная от лишних деталей самого пула:

public class Singleton
{
    private static Singleton instance= null;

    public static Singleton Shared
    {
        get { return Volatile.Read(ref instance) ?? EnsureSharedCreated(); }
    }

    private static Singleton EnsureSharedCreated()
    {
        Interlocked.CompareExchange(ref instance, Create(), null);
        return instance;
    }

    public static Singleton Create()
    {
        return new Singleton();
    }
    
    private Singleton()
    {
    }
    
    // ...
}