Вопрос

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

Код, о котором идет речь, такой:

// Make a temporary copy of the event to avoid possibility of
// a race condition if the last subscriber unsubscribes
// immediately after the null check and before the event is raised.
EventHandler<CustomEventArgs> handler = RaiseCustomEvent;

Я понимаю намерения кода, но не вижу, как именно эта строка делает копию чего-либо. Все, что он делает, это копирует reference ; на самом деле он не делает глубокую копию экземпляра делегата. Таким образом, на самом деле это вовсе не мешает состоянию гонки.

Я что-то упускаю здесь очевидное?

Это было полезно?

Решение

Делегаты являются неизменяемыми, поэтому ссылка, полученная в этом коде, гарантированно не изменится. Если пользователь подписывается или отписывается после нулевой проверки, новый делегат будет создан и настроен на событие. Однако, поскольку у вас есть ссылка на совершенно другой объект, и вы вызываете его, вам не нужно беспокоиться о том, что он имеет значение null.

Другие советы

Вы правы; это копирование ссылки.

Однако делегаты неизменны; когда вы добавляете обработчик к событию, создается новый делегат, объединяющий текущий обработчик (и) с новым, а затем назначаемый полю.

Экземпляр Delegate, на который ссылается поле, не может измениться, поэтому он избегает условия гонки.

Эрик Липперт уже рассмотрел это в очень подробном запись .

Это тоже из MSDN.

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

if (what! = null) what (); выглядит так, как будто это гарантирует, что независимо никогда не будет нулевым, когда вызывается независимо () , но это на самом деле не гарантирует, что в многопоточном сценарии. Другой поток может установить what = null между проверкой и вызовом.

Foo temp = whatever;
if (temp != null) temp();

Этот код исключает возможность разыменования с нулевым значением, поскольку temp является локальным и поэтому никогда не будет изменен другим потоком. Так что это предотвращает состояние гонки. Это не мешает всем соответствующим условиям гонки. Эрик Липперт сделал более детальное обсуждение некоторых других проблем с кодом.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top