Мне интересно, будет ли выполнение следующего вместо этого более эффективным, поскольку это позволит избежать непрерывной блокировки потока или скрытых проблем с ним?
Ваш вопрос: «Будет ли выигрыш в производительности, перейдя на опасный шаблон с низким уровнем блокировки?» То есть совершенно неправильный вопрос, чтобы задать. Никогда не рассуждайте так! Таким образом, это потраченное впустую время, потраченное впустую усилие и безумные, невозможные для отладки ошибки.
Правильный вопрос: «Мои измерения сильно указывают, что у меня есть проблема с производительностью?» ».
Если ответ «нет», значит, все готово.
Только если ответ «да», если вы задать следующий вопрос, который «Могу ли я устранить мою проблему производительности за счет устранения разногласий на замок?»
Если ответ «да», то устранить конфликт на замке и вернуться к первому вопросу.
Только если ответ «нет», тогда вы должны задать следующий вопрос: «Пойдет ли решение с низкой блокировкой, даст приемлемую производительность?»
Обратите внимание, что для ответа на этот вопрос, который должен быть «да», вы должны находиться в ситуации, когда штраф в размере десяти наносекунды, налагаемый незащищенным замком, является решающим фактором в вашей работе. Очень мало людей находятся в положении, когда десять или двадцать наносекунд слишком длинны.
В невероятно маловероятно, что ответ «да», вы должны перейти к следующему вопросу, который является «ли правильное выполнение блокировки с двойной проверкой устранить мою проблему производительности?»
Если блокировка с двойной проверкой не является достаточно быстрой, тогда реализация не является стартером. Вам придется решить вашу проблему по-другому.
Только если ответ на этот вопрос «да», если вы реализуете блокировку с двойной проверкой.
Теперь давайте прийти к вам актуальный вопрос:
есть ли скрытые проблемы с этим?
Ваша реализация верна. Однако, как только вы отклонились от блаженного паттерна, все ставки отключены. Например:
static object sync = new object();
static bool b = false;
static int x = 0;
static int GetIt()
{
if (!b)
{
lock(sync)
{
if (!b)
{
b = true;
x = ExpensiveComputation();
}
}
}
return x;
}
Это выглядит правильно, правда? Но это неверно! Рассмотрим путь с низкой блокировкой. Поскольку на этом пути нет препятствий, значение x может быть предварительно задано как ноль в одном потоке, а затем другой поток может выполняться и устанавливать b в true и x на 123, а затем исходный поток может извлекать b, get true и вернуть предварительно выбранный x.
Итак, какие решения? В моем порядке предпочтения:
- Не ленитесь. Инициализируйте статическое поле один раз и сделайте с ним.
- Используйте блаженный ленивый одноэлементный образец, задокументированный на веб-сайте Джона Скита.
- Использование
Lazy<T>
.
- Использовать блокировку с одной проверкой.
- Если вас не волнует, что синглтон находится в редких ситуациях, созданных дважды, а один отбрасывается, используйте
InterlockedCompareExchange
.
- Используйте благословенный двойной замок.
Вы можете сделать лучше: http://csharpindepth.com/Articles/General/Singleton.aspx – MarcinJuraszek
Хорошая ссылка, я ее читаю. –
Никто не мог сказать, является ли ваш данный код потокобезопасным или нет, не видя определения '_instance'. – Voo