Реализуйте потокобезопасный 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 lazy = new Lazy(() => 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;
}
}
}
Сами выбирайте какой вам больше нравится.