Вызывать события асинхронно - PullRequest
8 голосов
/ 18 марта 2009

Я ищу варианты выполнения асинхронной диспетчеризации событий в компоненте, который имеет много подписчиков на его события. Просматривая варианты, я наткнулся на этот пример:

public event ValueChangedEvent ValueChanged;
public void FireEventAsync(EventArgs e)
{
    Delegate[] delegates = ValueChanged.GetInvocationList();
    foreach (Delegate d in delegates)
    {
        ValueChangedEvent ev = (ValueChangedEvent)d;
        ev.BeginInvoke(e, null, null);
    }
}

Помимо более старого синтаксиса (пример был из .NET 1.1), мне кажется, что это серьезная утечка ресурсов. Нет метода завершения, опроса завершения или любого другого способа вызова EndInvoke.

Насколько я понимаю, каждый BeginInvoke должен иметь соответствующий EndInvoke. В противном случае существуют ожидающие AsyncResult экземпляры объектов, плавающие вокруг, а также (потенциально) исключения, которые были вызваны во время асинхронных событий.

Я понимаю, что это достаточно легко изменить, предоставив обратный вызов и выполнив EndInvoke, но если мне это не нужно. , .

Обработка асинхронных исключений - это совсем другое дело, и в сочетании с необходимостью синхронизации с потоком пользовательского интерфейса (т. Е. InvokeRequired и т. Д.) Вполне может быть реализована идея выполнения этих асинхронных уведомлений.

Итак, два вопроса:

  1. Правильно ли я считаю, что каждый BeginInvoke требует соответствующего EndInvoke?
  2. Помимо того, что я отметил выше, существуют ли другие подводные камни для выполнения асинхронных уведомлений о событиях в приложениях Windows Forms?

Ответы [ 3 ]

3 голосов
/ 18 марта 2009

Вызов BeginInvoke() должен быть в паре с EndInvoke(), но не выполнение этого не приведет к утечке ресурса. IAsyncResult, возвращаемое BeginInvoke(), будет собирать мусор.

Самой большой ошибкой в ​​этом коде является то, что вы сильно подвержены исключениям, завершающим работу приложения. Возможно, вы захотите обернуть вызов делегата в обработчик исключений и немного подумать о том, как вы хотите распространять возникающие исключения (сообщите первое, создайте статистическое исключение и т. Д.).

Вызов удаления с использованием BeginInvoke() приведет к удалению потока из очереди потока, чтобы начать запуск события. Это означает, что событие всегда будет запускать основной поток пользовательского интерфейса. Это может усложнить обработку некоторых сценариев обработчиков событий (например, обновление пользовательского интерфейса). Обработчики должны понимать, что им нужно вызывать SynchronizationContext.Send() или .Post() для синхронизации с основным потоком пользовательского интерфейса. Конечно, все другие ловушки многопоточного программирования также применимы.

1 голос
/ 01 апреля 2009

Подумав некоторое время об этом, я пришел к выводу, что, вероятно, плохая идея делать асинхронные события в элементах управления Windows Forms. События Windows Forms должны вызываться в потоке пользовательского интерфейса. В противном случае это создает чрезмерную нагрузку на клиентов и, возможно, создает беспорядок с AsyncResult объектами и асинхронными исключениями.

Чище позволить клиентам запускать собственную асинхронную обработку (используя BackgroundWorker или другой метод) или обрабатывать событие синхронно.

Есть исключения, конечно. Например, System.Timers.Timer вызывает событие Elapsed в потоке пула потоков. Но затем начальное уведомление приходит в потоке пула. Похоже, что общее правило таково: инициируйте события в том же потоке, который получил первоначальное уведомление. По крайней мере, это правило, которое работает лучше всего для меня. Таким образом, нет никаких вопросов о протекающих объектах.

0 голосов
/ 18 марта 2009
  1. Нет. EndInvoke требуется только если указан тип возвращаемого значения. Проверьте это: поток . Кроме того, я разместил эту ветку , которая частично связана.

  2. Я действительно не могу помочь вам с этим! :-) извините.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...