На очередном собеседовании второй вопрос, не первый, первый был про ссылочные типы данных, так вот второй воприк был такой:

Реализуйте потокобезопасный Singleton

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

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

				
			

Поскольку собеседование идет по Microsoft Teams, подсмотреть не составило труда и Я быстренько добавил замок для безопасности.

				
					// Стандартный и медленный
class Singletone2
{
    static Singletone2 _inst;
    private static object syncRoot = new object();
    public static Singletone2 Instance
    {
        get
        {
            lock (syncRoot)
            {
                if (_inst == null)
                    _inst = new Singletone2();
            }

            return _inst;
        }
    }
}
				
			

Но вот беда, у вопроса оказалось продолжение. Влияет ли замок на производительность? Этого я не ожидал, и профукал собес. Дальше уже продолжим по материалам из проверенных источников. Как выяснилось, ускорить можно добавив вторую проверку на null перед замком:

				
					// Быстрее чем стандарт
class Singletone3
{
    static Singletone3 _inst;
    private static object syncRoot = new object();
    public static Singletone3 Instance
    {
        get
        {
            if (_inst == null)
            {
                lock (syncRoot)
                {
                    if (_inst == null)
                        _inst = new Singletone3();
                }
            }

            return _inst;
        }
    }
}
				
			

Так как выполнение блокировки lock (syncRoot) жутко дорого, в этом варике мы предварили ее более простой проверкой указателя _inst == null. Это так называемая двойная проверка блокировки. Его цель-избежать дорогостоящей операции блокировки, которая понадобится только один раз (когда синглтон впервые доступен). Реализация такова, потому что она также должна гарантировать, что при инициализации синглтона не будет ошибок, возникающих в результате гонки потоков.

Следующий вариант реализации одиночки, называется Ленивый.

				
					// Ленивый
public sealed class Singleton4
{
    static readonly Lazy<Singleton4> lazy = new Lazy<Singleton4>(() => new Singleton4());
    private Singleton4() { }
    public static Singleton4 Instance => lazy.Value;
}
				
			

И последний вар от Рихтера, классика

				
					// Рихтер
public sealed class Singleton5
{
    private static readonly Object s_lock = new Object();
    private static Singleton5 instance = null;
    private Singleton5()
    {
    }

    public static Singleton5 Instance
    {
        get
        {
            if (instance != null) return instance;
            Monitor.Enter(s_lock);
            Singleton5 temp = new Singleton5();
            Interlocked.Exchange(ref instance, temp);
            Monitor.Exit(s_lock);
            return instance;
        }
    }
}
				
			

Сами выбирайте какой вам больше нравится.

Singleton – вопросы с собеседования
Tagged on:     

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.