Подтвердить что ты не робот

Является ли С# 6?. (Elvis op) потокобезопасным? Если да, то как?

Извините заранее: этот вопрос исходит от ядрового, нереформированного разработчика С++, пытающегося изучить продвинутый С#. Рассмотрим следующее:

if (myUserDefinedObject != null)
{
    myUserDefinedObject.ToString();
}

Это, очевидно, не является потокобезопасным. С другой стороны, я видел два учебника, которые говорят?. (Null условный оператор или "Elvis Operator" ), например,

myUserDefinedObject?.ToString();

Безопасный поток. Если компилятор не обертывает вокруг него обложки [mutex?] Под обложками (дрожь), я не понимаю, как это может быть правдой. Если эта идиома является потокобезопасной, может ли кто-нибудь указать мне техническое описание того, как это делается? Если он не защищен потоком, есть ли у кого-нибудь ссылка, которая на самом деле говорит, что это не так?

4b9b3361

Ответ 1

Я хочу уточнить ответ BJ Myers (правильный).

В С# событие можно рассматривать как поле типа делегата - так же, как свойство можно рассматривать как поле типа свойства, а значение этого "поля" может быть нулевым. Если вы находитесь в неудачной ситуации, когда обработчик события изменяется на один поток, а другой поток пытается его вызвать, вы можете попасть в ситуацию, когда:

if (this.SomeEvent != null) 
    this.SomeEvent( ... );

не является потокобезопасным. Значение может быть мутировано таким образом, чтобы оно не было равно null перед проверкой, null после проверки и сбой программы.

Обычный способ сделать это "threadafe", и я использую этот термин, желательно скопировать значение в локальный, а затем проверить локальный для null. Это имеет преимущество не сбой с нулевым разыменованием. Тем не менее, умный разработчик заметит, что еще есть гонка! Последовательность может быть

  • Обработчик ненулевого события, кэшированный в потоке A
  • Обработчик событий установлен на нуль в потоке B
  • Состояние, необходимое для обработчика событий, уничтожается в потоке B
  • Обработчик событий работает в потоке A и ужасно умирает

Таким образом, в этом смысле этот шаблон не является "потокобезопасным". Если вы находитесь в этой неудачной позиции , вы отвечаете за то, что соответствующая логика потоков реализована так, что этого не может произойти. Вы можете сделать это, как хотите. Если вы хотите (сомнительные) преимущества иметь возможность вызывать обработчик события в одном потоке при мутации события в другом потоке, то вам нужно заплатить либо за безопасность, либо за дело с ошибками условий гонки.

Я лично избегал бы такой ситуации, как чума, но я недостаточно умен, чтобы написать правильный многопоточный код.

Теперь, что касается актуального вопроса:

some_expression ?. ToString();

совпадает с

temp = some_expression
temp == null ? null : temp.ToString()

Является ли последний код "потоковым" по вашему мнению?

Ответ 2

От MSDN (внимание мое):

Другое использование для доступа к члену с нулевым состоянием - это вызов делегатов в потокобезопасном режиме с гораздо меньшим количеством кода. Для старого способа требуется следующий код:

var handler = this.PropertyChanged;
if (handler != null)
    handler(…)

Новый способ намного проще:

PropertyChanged?.Invoke(e)

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

Таким образом, здесь не задействована блокировка - безопасность потока обеспечивается путем создания локальной временной переменной, которая запрещает другой поток изменять эту переменную между нулевой проверкой и некоторой другой операцией.