Примечание
Для доступа к этой странице требуется авторизация. Вы можете попробовать войти или изменить каталоги.
Для доступа к этой странице требуется авторизация. Вы можете попробовать изменить каталоги.
Асинхронный шаблон на основе событий предоставляет эффективный способ демонстрации асинхронного поведения в классах с понятной семантикой событий и делегатов. Чтобы реализовать асинхронный шаблон на основе событий, необходимо следовать определенным требованиям поведения. В следующих разделах описаны требования и рекомендации, которые следует учитывать при реализации класса, следующего за асинхронным шаблоном на основе событий.
Общие сведения см. в разделе "Реализация асинхронного шаблона на основе событий".
Обязательные гарантии поведения
Если вы реализуете асинхронный шаблон на основе событий, необходимо предоставить ряд гарантий, чтобы обеспечить правильное поведение класса и клиенты вашего класса могут полагаться на такое поведение.
Завершение
Всегда вызывайте обработчик событий MethodNameCompleted при успешном завершении, ошибке или отмене. Приложения никогда не должны столкнуться с ситуацией, в которой они остаются бездействующими, и завершение никогда не происходит. Одно из исключений из этого правила заключается в том, что сама асинхронная операция разработана таким образом, чтобы она никогда не выполнялась.
Завершенное событие и аргументы события
Для каждого отдельного метода MethodNameAsync примените следующие требования к проектированию:
Определите событие MethodNameCompleted в том же классе, что и метод.
Определите класс и сопутствующий EventArgs делегат, который наследуется от класса, для события MethodNameAsyncCompletedEventArgs. Имя класса по умолчанию должно иметь имя метода MethodNameCompletedEventArgs.
Убедитесь, что EventArgs класс предназначен для возвращаемых значений метода MethodName. Если вы используете EventArgs класс, разработчики никогда не должны создавать результат.
В следующем примере кода показана хорошая и плохая реализация этого требования к проектированию соответственно.
// 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);
}
Не определяйте EventArgs класс для возвращаемых
void
методов. Вместо этого используйте экземпляр AsyncCompletedEventArgs класса.Убедитесь, что событие MethodNameCompleted всегда вызывается. Это событие должно вызываться при успешном завершении, ошибке или отмене. Приложения никогда не должны столкнуться с ситуацией, в которой они остаются бездействующими, и завершение никогда не происходит.
Убедитесь, что вы перехватываете все исключения, возникающие в асинхронной операции, и присваиваете захваченное исключение свойству Error .
Если возникла ошибка при выполнении задачи, результаты не должны быть доступны. Error Если свойство не является
null
, убедитесь, что при обращении к любому свойству в структуре EventArgs возникает исключение. RaiseExceptionIfNecessary Используйте метод для выполнения этой проверки.Смоделировать тайм-аут как ошибку. При истечении времени ожидания поднимите событие MethodNameCompleted и присвойте значение параметру TimeoutException.
Если класс поддерживает несколько одновременных вызовов, убедитесь, что событие MethodNameCompleted содержит соответствующий
userSuppliedState
объект.Убедитесь, что событие MethodNameCompleted вызывается в соответствующем потоке и в соответствующее время в жизненном цикле приложения. Дополнительные сведения см. в разделе «Потоки и контексты».
Одновременное выполнение операций
Если ваш класс поддерживает несколько одновременных вызовов, то предоставьте разработчику возможность отслеживать каждый вызов отдельно, определив перегрузку метода MethodNameAsync, которая принимает параметр состояния со значением объекта или идентификатор задачи, называемый
userSuppliedState
. Этот параметр всегда должен быть последним параметром в сигнатуре метода MethodNameAsync .Если класс определяет перегрузку MethodNameAsync , которая принимает параметр состояния с значением объекта или идентификатор задачи, обязательно отслеживайте время существования операции с идентификатором этой задачи и обязательно предоставьте его обратно в обработчик завершения. Есть вспомогательные классы для помощи. Дополнительные сведения об управлении параллелизмом см. в статье "Практическое руководство. Реализация компонента, поддерживающего асинхронный шаблон на основе событий".
Если класс определяет метод MethodNameAsync без параметра состояния и не поддерживает несколько одновременных вызовов, убедитесь, что любая попытка вызова MethodNameAsync до завершения вызова предыдущего вызова MethodNameAsync вызывает значениеInvalidOperationException.
Как правило, не выбрасывайте исключение, если метод MethodNameAsync вызывается несколько раз без использования параметра
userSuppliedState
, что приводит к наличию нескольких активных операций. Вы можете вызвать исключение, если класс явно не может обработать эту ситуацию, но предположим, что разработчики могут справиться с этими несколькими неразличимыми обратными вызовами.
Доступ к результатам
Если во время выполнения асинхронной операции произошла ошибка, результаты не должны быть доступны. Убедитесь, что доступ к любому свойству в AsyncCompletedEventArgs, когда Error не является
null
, возбуждает исключение со ссылкой на Error. Класс AsyncCompletedEventArgs предоставляет RaiseExceptionIfNecessary метод для этой цели.Убедитесь, что любая попытка получить доступ к результату вызывает InvalidOperationException, указывающий, что операция была отменена. AsyncCompletedEventArgs.RaiseExceptionIfNecessary Используйте метод для выполнения этой проверки.
Отчеты о ходе выполнения
По возможности, поддержите наличие отчетов о ходе выполнения. Это позволяет разработчикам предоставлять более эффективное взаимодействие с пользователем приложения при использовании класса.
Если вы реализуете событие ProgressChanged или MethodNameProgressChanged , убедитесь, что для определенной асинхронной операции после вызова события MethodNameCompleted операции нет таких событий.
Если стандарт ProgressChangedEventArgs заполняется, следите за тем, чтобы ProgressPercentage всегда можно было интерпретировать как процент. Процент не должен быть точным, но он должен представлять процент. Если метрика отчетов о ходе выполнения должна быть чем-то другим, кроме процента, создайте класс, наследуемый от ProgressChangedEventArgs класса и оставьте ProgressPercentage на 0. Избегайте использования метрики отчетов, отличной от процента.
Убедитесь, что
ProgressChanged
событие вызывается в соответствующем потоке и в соответствующее время в жизненном цикле приложения. Дополнительные сведения см. в разделе «Потоки и контексты».
Реализация IsBusy
Не предоставляйте свойство
IsBusy
, если класс поддерживает несколько одновременных вызовов. Например, прокси-серверы веб-службы XML не предоставляютIsBusy
свойство, так как они поддерживают несколько одновременных вызовов асинхронных методов.Свойство
IsBusy
должно возвращатьсяtrue
после вызова метода AsyncmethodName и до вызова события MethodNameCompleted. В противном случае он должен вернутьfalse
. Компоненты BackgroundWorker и WebClient — это примеры классов, которые предоставляют свойствоIsBusy
.
Отмена
Поддержите отмену, если это возможно. Это позволяет разработчикам предоставлять более эффективное взаимодействие с пользователем приложения при использовании класса.
В случае отмены задайте Cancelled флаг в объекте AsyncCompletedEventArgs .
Убедитесь, что любая попытка получить доступ к результату вызывает InvalidOperationException, указывающий, что операция была отменена. AsyncCompletedEventArgs.RaiseExceptionIfNecessary Используйте метод для выполнения этой проверки.
Убедитесь, что вызовы метода отмены всегда выполняются успешно и не приводят к возникновению исключений. Как правило, клиент не уведомляется о том, можно ли отменить операцию в данный момент времени и не уведомляется о том, была ли успешной ранее выполненная отмена. Однако приложение всегда будет уведомлено об успешной отмене, так как оно участвует в процессе завершения.
Вызов события MethodNameCompleted при отмене операции.
Ошибки и исключения
- Перехватите любые исключения, возникающие в асинхронной операции, и установите значение свойства AsyncCompletedEventArgs.Error для этого исключения.
Потоки и контексты
Для правильной работы класса важно, чтобы обработчики событий клиента вызываются в соответствующем потоке или контексте для данной модели приложения, включая ASP.NET и приложения Windows Forms. Предоставляются два важных вспомогательных класса, чтобы обеспечить правильность поведения асинхронного класса в любой модели приложения: AsyncOperation и AsyncOperationManager.
AsyncOperationManager предоставляет один метод, CreateOperationкоторый возвращает значение AsyncOperation. Ваш метод MethodNameAsync вызывает CreateOperation, и ваш класс использует возвращенный AsyncOperation для отслеживания срока существования асинхронной задачи.
Чтобы сообщить о ходе выполнения, добавочных результатах и завершении клиенту, вызовите методы Post и OperationCompleted на AsyncOperation. AsyncOperation отвечает за управление передачей вызовов обработчикам событий клиента в соответствующий поток или контекст.
Замечание
Эти правила можно обойти, если вы явно хотите пойти против политики модели приложения, но при этом воспользоваться другими преимуществами использования асинхронного шаблона на основе событий. Например, может возникнуть необходимость в том, чтобы класс, работающий с Windows Forms, был свободно управляемым потоками. Вы можете создать свободный многопоточный класс, если разработчики понимают подразумеваемые ограничения. Консольные приложения не синхронизируют выполнение вызовов Post . Это может привести к возникновению событий ProgressChanged
не по порядку. Если вы хотите сериализовать выполнение вызовов Post , реализуйте и установите System.Threading.SynchronizationContext класс.
Дополнительные сведения об использовании AsyncOperation и включении асинхронных операций см. в статье AsyncOperationManager".
Руководящие принципы
В идеале вызов каждого метода должен быть независимым от других. Следует избегать связывания вызовов с общими ресурсами. Если ресурсы должны будут использоваться совместно при вызовах, вам потребуется обеспечить соответствующий механизм синхронизации в вашей реализации.
Проекты, требующие от клиента реализации синхронизации, не рекомендуется. Например, у вас может быть асинхронный метод, который получает глобальный статический объект в качестве параметра; несколько одновременных вызовов такого метода могут привести к повреждению данных или взаимоблокировкам.
Если вы реализуете метод с перегрузкой с несколькими вызовами (
userState
в сигнатуре), классу потребуется управлять коллекцией состояний пользователей или идентификаторами задач и соответствующими ожидающими операциями. Эта коллекция должна быть защищена регионамиlock
, так как различные вызовы добавляют и удаляютuserState
объекты в коллекции.Рекомендуется повторно использовать
CompletedEventArgs
классы, где это возможно и необходимо. В этом случае именование не соответствует имени метода, так как заданный делегат и EventArgs тип не привязаны к одному методу. Однако принуждать разработчиков приводить значение, полученное из свойства в EventArgs, никогда не допустимо.Если вы создаете класс, производный от класса Component, не реализуйте или устанавливайте собственный класс SynchronizationContext. Модели приложений, а не компоненты, управляют используемым SynchronizationContext.
При использовании многопоточности любого рода вы потенциально подвергаетесь очень серьезным и сложным ошибкам. Перед реализацией любого решения, использующего многопоточность, ознакомьтесь с рекомендациями по управляемому потоку.
См. также
- AsyncOperation
- AsyncOperationManager
- AsyncCompletedEventArgs
- ProgressChangedEventArgs
- BackgroundWorker
- Реализация асинхронного шаблона на основе событий
- Шаблон асинхронного программирования на основе событий (EAP)
- Выбор момента реализации асинхронного шаблона на основе событий
- Рекомендации по реализации асинхронного шаблона на основе событий
- Практическое руководство. Использование компонентов, поддерживающих асинхронный шаблон на основе событий
- Практическое руководство. Реализация компонента, поддерживающего асинхронный шаблон на основе событий