Поделиться через


Рекомендации по реализации асинхронной модели, основанной на событиях

Обновлен: Ноябрь 2007

Асинхронная модель, основанная на событиях, предоставляет эффективный способ предоставления асинхронного поведения в классах с известной семантикой событий и делегатов. Чтобы реализовать асинхронную модель, основанную на событиях, необходимо выполнить некоторые особые требования к поведению. В следующих разделах приведены требования и руководства, которые следует учесть при реализации класса, который следует за асинхронной моделью, основанной на событиях.

Общие сведения см. в разделе Реализация асинхронной модели, основанной на событиях.

В следующем списке приведены рекомендации, описанные в этом разделе:

  • Требуемые гарантии поведения

  • Выполнение

  • Выполненное событие и EventArgs

  • Одновременно выполняемые операции

  • Доступ к результатам

  • Отчет о ходе выполнения

  • Реализация IsBusy

  • Отмена

  • Ошибки и исключения

  • Поточность и контексты

  • Правила

Требуемые гарантии поведения

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

Выполнение

Всегда вызывайте обработчик события имя_методаCompleted при успешном завершении, ошибке или отмене. Приложения никогда не должны попадать в ситуацию, в которой они будут простаивать и завершение никогда не произойдет. Единственным исключением из этого правила является наличие такой асинхронной операции, которая разработана таким образом, что не может завершиться сама.

Выполненное событие и EventArgs

Для каждого отдельного метода имя_методаAsync используйте следующие требования к разработке:

  • Определите событие имя_методаCompleted для того же класса, что и метод.

  • Определите класс EventArgs и сопутствующий делегат для события имя_методаCompleted, который является производным из класса AsyncCompletedEventArgs. Имя класса по умолчанию должно быть в форме имя_методаCompletedEventArgs.

  • Убедитесь, что класс EventArgs является особым для возвращаемых значений метода имя_метода. При использовании класса EventArgs никогда не следует требовать от разработчиков приведения результатов.

    В следующем примере кода показана правильная и неправильная реализация этого требования к проектированию.

[C#]

// Good design
private void Form1_MethodNameCompleted(object sender, xxxCompletedEventArgs e) 
{ 
    DemoType result = e.Result;
}

// Bad design
private void Form1_MethodNameCompleted(object sender, MethodNameCompletedEventArgs e) 
{ 
    DemoType result = (DemoType)(e.Result);
}

Одновременно выполняемые операции

  • Если класс поддерживает несколько одновременных вызовов, разрешите разработчику отслеживать каждый вызов отдельно путем определения перегрузки имя_методаAsync, которая принимает параметр состояния объекта или идентификатор задачи userSuppliedState. Этот параметр всегда должен быть последним параметром в подписи метода имя_методаAsync.

  • Если класс определяет перегрузку имя_методаAsync, которая принимает параметр состояния объекта или идентификатор задачи, отследите жизненный цикл операции с этим идентификатором задачи и предоставьте его обратно в обработчик завершения. Можно использовать вспомогательные классы. Дополнительные сведения об управлении параллельным выполнением см. в разделе Пошаговое руководство. Реализация компонента, поддерживающего асинхронную модель, основанную на событиях.

  • Если в классе определяется метод имя_методаAsync без параметра состояния и этот класс не поддерживает несколько одновременных вызовов, убедитесь, что любые попытки вызова имя_методаAsync до завершения предыдущего вызова имя_методаAsync приведут к созданию исключения InvalidOperationException.

  • В целом, не создавайте исключение, если метод имя_методаAsync без параметра userSuppliedState был вызван несколько раз, чтобы одновременно выполнялись несколько незавершенных операций. Можно создать исключение, если класс явно не может работать в этой ситуации, однако учитывайте, что разработчики могут обрабатывать эти неотличимые обратные вызовы.

Доступ к результатам

  • Если во время выполнения асинхронной операции произошла ошибка, доступ к результатам предоставлен не будет. Убедитесь, что доступ к любому свойству в AsyncCompletedEventArgs при значении свойства Error не равном null, приводит к созданию исключения, на которое ссылается свойство Error. Для этих целей класс AsyncCompletedEventArgs предоставляет метод RaiseExceptionIfNecessary.

  • Убедитесь, что любая попытка доступа к результатам приводит к созданию исключения InvalidOperationException, в котором говорится об отмене операции. Для выполнения такой проверки используйте метод AsyncCompletedEventArgs.RaiseExceptionIfNecessary.

Отчет о ходе выполнения

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

  • При реализации события ProgressChanged/имя_методаProgressChanged убедитесь, что отсутствуют события, созданные для определенной асинхронной операции после создания события имя_методаCompleted, относящегося к этой операции.

  • Если заполняется стандартный объект ProgressChangedEventArgs, убедитесь, что свойство ProgressPercentage всегда может восприниматься как проценты. Проценты не должны быть точными, но должны быть представлены как проценты. Если шкала хода выполнения выражается не в процентах, следует сделать производный класс из ProgressChangedEventArgs и оставить ProgressPercentage равным 0. Избегайте использования шкалы хода выполнения, отличной от процентов.

  • Убедитесь, что событие ProgressChanged создается в соответствующем потоке и в подходящее время жизненного цикла приложения. Дополнительные сведения см. в подразделе, посвященном поточности и контекстам.

Реализация IsBusy

  • Не предоставляйте свойство IsBusy, если класс поддерживает несколько одновременных вызовов. Например, прокси веб-служб XML не предоставляют свойство IsBusy, так как эти прокси поддерживают несколько одновременных вызовов асинхронных методов.

  • Свойство IsBusy должно возвращать значение true после вызова метода название_методаAsync и до создания события название_методаCompleted. В противном случае возвращается значение false. Компоненты BackgroundWorker и WebClient являются примерами классов, которые предоставляют свойство IsBusy.

Отмена

  • При возможности поддерживайте отмену. Это позволяет разработчикам облегчить работу пользователей при использовании пользовательского класса.

  • В случае отмены установите флаг Cancelled в объекте AsyncCompletedEventArgs.

  • Убедитесь, что любая попытка доступа к результатам приводит к созданию исключения InvalidOperationException, в котором говорится об отмене операции. Для выполнения такой проверки используйте метод AsyncCompletedEventArgs.RaiseExceptionIfNecessary.

  • Убедитесь, что вызовы метода отмены всегда возвращаются успешно и никогда не создают исключений. В целом, клиент не уведомляется о том, поддерживает ли операция отмену в любое время, а также о том, завершилась ли успешно предыдущая отмена операции. Однако приложение всегда будет уведомляться об успешности выполнения отмены, потому что приложение принимает участие в состоянии выполнения.

  • Создайте событие имя_методаCompleted при отмене операции.

Ошибки и исключения

  • Перехватите любые исключения, которые возникают в асинхронной операции, и задайте значение свойства AsyncCompletedEventArgs.Error для этого исключения.

Поточность и контексты

Для правильной работы класса очень важно, чтобы обработчики событий клиента вызывались в правильном потоке или контексте для данной модели приложения, включая приложения ASP.NET и Windows Forms. Два важных вспомогательных класса предоставлены для того, чтобы гарантировать правильное поведение пользовательского асинхронного класса в любой модели приложения. Это классы AsyncOperation и AsyncOperationManager.

AsyncOperationManager предоставляет один метод CreateOperation, который возвращает AsyncOperation. Пользовательский метод имя_методаAsync вызывает метод CreateOperation, а класс использует возвращенный объект AsyncOperation для отслеживания жизненного цикла асинхронной задачи.

Для предоставления клиенту отчета о ходе выполнения, добавочных результатах и завершении вызовите методы Post и OperationCompleted для AsyncOperation. Объект AsyncOperation несет ответственность за маршалинг вызовов в клиентские обработчики событий в правильном потоке или контексте.

ms228974.alert_note(ru-ru,VS.90).gifПримечание.

Можно обойти эти правила, если необходимо явно нарушить политику модели приложения, и при этом получить преимущества использования асинхронной модели, основанной на событиях. Например, может понадобиться, чтобы класс, выполняющийся в Windows Forms, был свободно-потоковым. Можно создать свободно-потоковый класс, если разработчики понимают применимые ограничения. Консольные приложения не синхронизируют выполнение вызовов Post. Это может нарушить порядок возникновения событий ProgressChanged. Если требуется сериализованное выполнение вызовов Post, следует реализовать и вызвать класс System.Threading.SynchronizationContext.

Дополнительные сведения об использовании AsyncOperation и AsyncOperationManager для разрешения асинхронных операций см. Пошаговое руководство. Реализация компонента, поддерживающего асинхронную модель, основанную на событиях.

Правила

  • В идеале, каждый вызов метода должен не зависеть от других вызовов. Следует избегать связывания вызовов с общими ресурсами. Если ресурсы должны совместно использоваться разными вызовами, следует предоставить в реализации допустимый механизм синхронизации.

  • Настоятельно рекомендуются разработки, в которых клиент должен реализовывать синхронизацию. Например, можно иметь асинхронный метод, который получает глобальный статический объект в качестве параметра; несколько одновременных вызовов такого метода может привести к повреждению данных или к взаимоблокировкам.

  • Если реализовывать метод с перегрузкой нескольких вызовов (userState в подписи), класс должен управлять коллекцией пользовательских состояний или идентификаторов задач, а также соответствующими операциями в очереди. Эта коллекция должна быть защищена с помощью областей lock, так как различные вызовы приводят к добавлению и удалению объектов userState в коллекции.

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

  • При создании класса, который является производным из Component, не реализовывайте и не устанавливайте собственный класс SynchronizationContext. Используемым объектом SynchronizationContext управляют модели приложения, а не компоненты.

  • При использовании многопоточных программ возникает опасность весьма серьезных и сложных ошибок. Ознакомьтесь с разделом Рекомендации по работе с потоками перед реализацией любых решений, использующих многопоточность.

См. также

Задачи

Практическое руководство. Использование компонентов, поддерживающих асинхронную модель, основанную на событиях

Пошаговое руководство. Реализация компонента, поддерживающего асинхронную модель, основанную на событиях

Основные понятия

Реализация асинхронной модели, основанной на событиях

Определение, когда следует реализовать асинхронную модель, основанную на событиях

Рекомендации по реализации асинхронной модели, основанной на событиях

Ссылки

AsyncOperation

AsyncOperationManager

AsyncCompletedEventArgs

ProgressChangedEventArgs

BackgroundWorker

Другие ресурсы

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