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

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

Итак, я прочитал, что вместо прямого вызова события

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

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

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

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

4b9b3361

Ответ 1

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

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

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

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

Ответ 2

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

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

var handler = SomeEvent;

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

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

Ответ 3

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

Ответ 4

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

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