Обработчики событий не безопасны для потоков?
Так что я прочитал об этом вместо того, чтобы вызывать событие напрямую
if (SomeEvent != null)
SomeEvent(this, null);
я должен делать
SomeEventHandler temp = SomeEvent;
if (temp != null)
temp(this, null);
Почему это так? Как вторая версия становится поточно-ориентированной? Какова лучшая практика?
4 ответа
События действительно синтаксический сахар в списке делегатов. Когда вы вызываете событие, это действительно итерация по этому списку и вызов каждого делегата с параметрами, которые вы передали.
Проблема с потоками заключается в том, что они могут добавлять или удалять элементы из этой коллекции, подписываясь / отписываясь. Если они сделают это, пока вы выполняете итерацию коллекции, это вызовет проблемы (я думаю, что выдается исключение)
Намерение состоит в том, чтобы скопировать список перед его повторением, чтобы вы были защищены от изменений в списке.
Примечание: однако теперь ваш слушатель может быть вызван даже после того, как вы отменили подписку, поэтому вы должны убедиться, что вы обработали это в своем коде слушателя.
ИМО, в других ответах не хватает одной ключевой детали - что делегаты (и, следовательно, события) являются неизменными. Значение этого заключается в том, что подписка или отмена подписки на обработчик событий не просто добавляет / удаляет список, а скорее заменяет список новым с дополнительным (или одним меньшим) элементом на нем.
Поскольку ссылки являются атомарными, это означает, что в тот момент, когда вы делаете:
var handler = SomeEvent;
теперь у вас есть жесткий экземпляр, который не может измениться, даже если в следующую пикосекунду другой поток отписывается (в результате чего фактическое поле события становится null
).
Итак, вы проверяете на null и вызываете его, и все хорошо. Заметьте, конечно, что все еще есть запутанный сценарий события, возникающего на объекте, который думает, что он отписался пикосекунду назад!
Лучшая практика - вторая форма. Причина в том, что другой поток может обнулять или изменять SomeEvent
между 'if
'тест и вызов.
Вот хорошая статья о событиях.NET и условиях гонки с потоками. Он охватывает некоторые распространенные сценарии и содержит несколько хороших ссылок.
Надеюсь это поможет.