Добавить делегат в безопасность событий - потоков
Можно выполнить следующий код из нескольких потоков одновременно.
this._sequencer.Completed += OnActivityFinished;
Можно ли потокобезопасно добавлять делегат в обработчик событий из нескольких потоков?
Безопасно ли потоковое удаление делегата из обработчика событий из нескольких потоков?
Каков самый простой и удобный способ обеспечения безопасности этого потока?
Ответы
Ответ 1
Если вы не указали свои собственные обработчики добавления/удаления событий, компилятор С# генерирует этот add handler (реконструированный .NET Reflector):
public void add_MyEvent(EventHandler value)
{
EventHandler handler2;
EventHandler myEvent = this.MyEvent;
do
{
handler2 = myEvent;
EventHandler handler3 = (EventHandler) Delegate.Combine(handler2, value);
myEvent = Interlocked.CompareExchange<EventHandler>(ref this.MyEvent, handler3, handler2);
}
while (myEvent != handler2);
}
и удалить обработчик, который выглядит так же, но с Delegate.Remove
вместо Delegate.Combine
.
Обратите внимание на использование Interlocked.CompareExchange
? Это предотвращает условие гонки между обновлением поля поддержки событий и чтением из него. Таким образом, он является потокобезопасным.
Ответ 2
для полевых событий Добавление/удаление обработчиков является потокобезопасным. Из спецификации:
При компиляции полевого события компилятор автоматически создает хранилище для хранения делегата и создает аксессоры для события, которые добавляют или удаляют обработчики событий в поле делегата. Чтобы быть потокобезопасными, операции добавления или удаления выполняются при удерживании блокировки (§8.12) для объекта-объекта для события экземпляра или объекта типа (§7.6.10.6) для статического события.
Однако это верно для С# 3.0 и меньше, в компиляторе С# 4.0 генерируется незаблокированная реализация с использованием блокированных подпрограмм (но спецификация остается той же - ошибка?)
В пользовательских реализациях никто не может точно сказать... кроме, может быть, автора кода:)
Ответ 3
Это зависит от реализации мероприятия, если честно.
Полевые события, генерируемые компилятором С#, являются потокобезопасными, но если это пользовательское событие, кто знает?
Обратите внимание, что в многопоточном приложении вы должны ожидать условия гонки между добавлением/удалением обработчика и запуском события... например, событие может начать срабатывать, вы можете отказаться от подписки, и ваш обработчик все равно будет после этого отменяется подписка.