2010-08-19 2 views
23

Можно выполнить следующий код из нескольких потоков одновременно.Добавить делегат на событие - безопасность потока

this._sequencer.Completed += OnActivityFinished; 

Можно ли потокобезопасно добавлять делегат в обработчик событий из нескольких потоков?

Можно ли поточно удалить делегат из обработчика событий из нескольких потоков?

Каков самый простой и удобный способ обеспечения безопасности этого потока?

ответ

31

Если вы не укажете свое собственное событие добавить/удалить обработчик, компилятор с # генерирует этот добавить обработчик (реконструированного по .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); 
} 

и a удалить обработчик, который выглядит так же, но с Delegate.Remove вместо Delegate.Combine.

Обратите внимание на использование Interlocked.CompareExchange? Это предотвращает условие гонки между обновлением поля поддержки события и чтением из него. Таким образом, он является потокобезопасным.

+2

Небольшое уточнение - это реализация в .NET 4, до этого она использовала 'lock (this)' (также см. Ответ desco) –

+0

@ohadsc Я только что создал событие и скомпилировал с .net 2, и он все еще создал блокировку код, определенный timwi – Simon

+4

@Simon, вы правы, это функция компилятора, а не среда исполнения. Я должен был сказать «это реализация в компиляторе C# 4», –

4

Это зависит от реализации мероприятия, если быть честным.

Полевые события, сгенерированные компилятором C#, являются потокобезопасными, но если это настраиваемое событие, кто знает?

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

5

для полевых событий Добавление/удаление обработчиков является потокобезопасным. Из спецификации:

При компиляции полевого события компилятор автоматически создает хранилище для хранения делегата и создает аксессоры для события, которые добавляют или удаляют обработчики событий в поле делегирования. Чтобы быть потокобезопасными, операции добавления или удаления выполняются, удерживая блокировку (§8.12) на содержащем объекте для события экземпляра или объект типа (§7.6.10.6) для статического события.

Однако это верно для C# 3.0 и меньших, в C# 4.0 компилятора генерирует реализацию безблокировочной с использованием ВЗАИМОСВЯЗАННЫХ процедур (но спецификация остается тем же - ошибка)

В пользовательских внедрениях никто не может точно сказать, ... кроме того, может быть, автор кода :)

+0

Я считаю, что спецификация была обновлена, но обновленная версия еще не опубликована. –

+0

Возможно, я имел в виду имеющуюся версию (опубликовать дату \t 4/19/2010) - http://www.microsoft.com/downloads/details.aspx?FamilyID=dfbf523c-f98c-4804-afbd-459e846b268e&displaylang=en – desco

Смежные вопросы