Обработчики событий не безопасны для потоков?

Так что я прочитал об этом вместо того, чтобы вызывать событие напрямую

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

я должен делать

SomeEventHandler temp = SomeEvent;
if (temp != null)
    temp(this, null);

Почему это так? Как вторая версия становится поточно-ориентированной? Какова лучшая практика?

4 ответа

Решение

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

Проблема с потоками заключается в том, что они могут добавлять или удалять элементы из этой коллекции, подписываясь / отписываясь. Если они сделают это, пока вы выполняете итерацию коллекции, это вызовет проблемы (я думаю, что выдается исключение)

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

Примечание: однако теперь ваш слушатель может быть вызван даже после того, как вы отменили подписку, поэтому вы должны убедиться, что вы обработали это в своем коде слушателя.

ИМО, в других ответах не хватает одной ключевой детали - что делегаты (и, следовательно, события) являются неизменными. Значение этого заключается в том, что подписка или отмена подписки на обработчик событий не просто добавляет / удаляет список, а скорее заменяет список новым с дополнительным (или одним меньшим) элементом на нем.

Поскольку ссылки являются атомарными, это означает, что в тот момент, когда вы делаете:

var handler = SomeEvent;

теперь у вас есть жесткий экземпляр, который не может измениться, даже если в следующую пикосекунду другой поток отписывается (в результате чего фактическое поле события становится null).

Итак, вы проверяете на null и вызываете его, и все хорошо. Заметьте, конечно, что все еще есть запутанный сценарий события, возникающего на объекте, который думает, что он отписался пикосекунду назад!

Лучшая практика - вторая форма. Причина в том, что другой поток может обнулять или изменять SomeEvent между 'if'тест и вызов.

Вот хорошая статья о событиях.NET и условиях гонки с потоками. Он охватывает некоторые распространенные сценарии и содержит несколько хороших ссылок.

Надеюсь это поможет.

Другие вопросы по тегам