Является ли С# '??' безопасность оператора?
Всем известно, что это не потокобезопасно:
public StringBuilder Builder
{
get
{
if (_builder != null)
_builder = new StringBuilder();
return _builder;
}
}
Как насчет этого?
public StringBuilder Builder
{
get { return _builder ?? (_builder = new StringBuilder()); }
}
Ответы
Ответ 1
НАЧАТЬ EDIT
Основываясь на вашем отредактированном заголовке, сам оператор с нулевым коалесцированием представляется потокобезопасным (см. анализ Phil Haack). Однако представляется, что он не гарантирует против потенциальных множественных вызовов конструктору StringBuilder.
END EDIT
У вас есть большая проблема с потоковой обработкой, и это значит, что само свойство Builder представляет состояние, которое может быть разделено между потоками. Даже если вы делаете ленивый поток инициализации безопасным, нет никакой гарантии, что методы, потребляющие Builder, делают это безопасным потоком.
// below code makes the getter thread safe
private object builderConstructionSynch = new object();
public StringBuilder Builder
{
get
{
lock (builderConstructionSynch)
{
if (_builder == null) _builder = new StringBuilder();
}
return _builder;
}
}
Вышеизложенное предотвратит проблему потоковой передачи в ленивой инициализации _builder, но если вы не синхронизируете свои вызовы с методами экземпляра StringBuilder, вам не гарантируется безопасность потоков в любых методах, которые потребляют свойство Builder. Это связано с тем, что методы экземпляров в StringBuilder не были предназначены для обеспечения потоковой безопасности. См. Ниже текст на странице
Ответ 2
Это не более или менее поточно-безопасное; вы все равно можете иметь два потока, выполняющих нулевую проверку одновременно, тем самым создавая отдельные объекты и не видя другого.
Ответ 3
НЕТ для обеих версий
Ответ 4
Нет, ни атомные
Ответ 5
Ответы правильные, оба не являются потокобезопасными. Фактически, они в основном эквивалентны, а оператор ??
- просто магия компилятора, чтобы сделать код более компактным. Вам нужно использовать какой-то механизм синхронизации, если вы хотите, чтобы это стало потокобезопасным.
Ответ 6
Я сам не тестировал этот подход, но если вы хотите, чтобы безопасность потоков без накладных расходов на схему блокировки, и вас не беспокоит потенциальное создание и отбрасывание экземпляра объекта, вы можете попробовать следующее:
using System.Threading;
public StringBuilder Builder
{
get
{
if (_builder != null)
Interlocked.CompareExchange( ref _builder, new StringBuilder(), null );
return _builder;
}
}
Вызов CompareExchange() будет выполнять атомную замену значения в _builder новым экземпляром StringBuilder, только если _builder == null. Все методы класса Interlocked гарантируют, что НЕ будут вытеснены переключателями потоков.