Вызов EventHandler - PullRequest
       8

Вызов EventHandler

7 голосов
/ 11 августа 2011

У меня есть следующий EventHandler:

private EventHandler<MyEventArgs> _myEventHandler;
public event EventHandler<MyEventArgs> MyEvent
{
  add { _myEventHandler += value; }
  remove { _myEventHandler -= value; }
}  

Может ли кто-нибудь объяснить разницу между следующими фрагментами?
Фрагмент EventHandler (A):

//Snippet A:
if (_myEventHandler != null)
{
  _myEventHandler(new MyEventArgs());
}

Фрагмент BeginInvoke (B):

//Snippet B:
if (_myEventHandler != null)
{
  _myEventHandler.BeginInvoke(new MyEventArgs(), ar =>
  {
    var del = (EventHandler<MyEventArgs>)ar.AsyncState;
    del.EndInvoke(ar);
  }, _myEventHandler);
}

Для пояснения: в чем разница между вызовом EventHandler «как есть» и использованием BeginInvoke?

Ответы [ 2 ]

13 голосов
/ 11 августа 2011

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

Также обратите внимание, что строго говоря вы должны сделать снимок значения обработчика события- это особенно true, если (через Begin*) вы имеете дело с потоками.

var tmp = _myEventHandler;
if(tmp != null) {
    tmp(sender, args);
}

Также обратите внимание, что ваша подписка на событие сама не является потоком-безопасный;Опять же, это имеет значение, только если вы имеете дело с многопоточностью, но встроенное поле-событие является поточно-безопасным:

public event EventHandler<MyEventArgs> MyEvent; // <===== done; nothing more

Здесь избегаются следующие проблемы:

  • с помощью моментального снимка мы избегаем риска, когда последний подписчик отписался между нулевой проверкой и вызовом (это означает, что они могут получить событие, которого они не ожидали, но это означает, что мы не убиваемподнимающий поток)
  • с изменением события, подобного полю, мы избегаем риска потери подписок / отписок, когда два потока делают это одновременно
5 голосов
/ 11 августа 2011

BeginInvoke() вызов немедленно возвращает управление вызывающему потоку и запускает делегат в отдельном потоке от ThreadPool, так что это будет какое-то асинхронное выполнение.

...