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

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

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

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

Для простых асинхронных операций можно найти подходящий компонент BackgroundWorker. Дополнительные сведения о BackgroundWorker см. в разделе Практическое руководство. Фоновое выполнение операции.

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

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

  • Именование асинхронных методов

  • Дополнительная поддержка отмены

  • Дополнительная поддержка свойства IsBusy

  • Дополнительное предоставление поддержки для отображения хода выполнения

  • Дополнительное предоставление поддержки для возврата добавочных результатов

  • Обработка параметров Out и Ref в методах

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

Рассмотрите возможность реализации асинхронной модели, основанной на событиях, в следующих случаях:

  • Клиенты пользовательского класса могут не нуждаться в объектах WaitHandle и IAsyncResult, доступных для асинхронных операций. Это означает, что опрос и методы WaitAll или WaitAny должны будут создаваться клиентом.

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

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

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

Именование асинхронных методов

Для каждого синхронного метода имя_метода, для которого следует предоставить асинхронный эквивалент, выполните следующие действия:

Определите метод имя_методаAsync, который:

  • Возвращает void.

  • Принимает те же параметры, что и метод имя_метода.

  • Принимает несколько вызовов.

Дополнительно определите перегрузку имя_методаAsync, идентичный имя_методаAsync, добавляя параметр, зависящий от объекта, с именем userState. Выполните это, если следует управлять несколькими параллельными вызовами метода. В этом случае значение userState будет возвращено всем обработчикам события для различения вызовов метода. Также можно выполнить это действие для предоставления места размещения пользовательского состояния для последующего получения.

Для каждой отдельной сигнатуры метода имя_методаAsync:

  1. Определите следующее событие в том же классе как метод:

    Public Event MethodNameCompleted As MethodNameCompletedEventHandler
    
    public event MethodNameCompletedEventHandler MethodNameCompleted;
    
  2. Определите следующий делегат и AsyncCompletedEventArgs. Они, скорее всего, будут определены вне самого класса, но в том же пространстве имен.

    Public Delegate Sub MethodNameCompletedEventHandler( _
        ByVal sender As Object, _
        ByVal e As MethodNameCompletedEventArgs)
    
    Public Class MethodNameCompletedEventArgs
        Inherits System.ComponentModel.AsyncCompletedEventArgs
    Public ReadOnly Property Result() As MyReturnType
    End Property
    
    public delegate void MethodNameCompletedEventHandler(object sender, 
        MethodNameCompletedEventArgs e);
    
    public class MethodNameCompletedEventArgs : System.ComponentModel.AsyncCompletedEventArgs
    {
        public MyReturnType Result { get; }
    }
    
    • Убедитесь, что класс имя_методаCompletedEventArgs предоставляет свои элементы в качестве свойств, доступных только для чтения, а не полей, так как поля предотвращают привязку данных.

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

      ПримечаниеПримечание

      Вполне допустимо (если это необходимо) повторно использовать делегат и типы AsyncCompletedEventArgs.В этом случае именования не будет согласовываться с именем метода, так как данный делегат и AsyncCompletedEventArgs не будут привязаны к одному методу.

Дополнительная поддержка отмены

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

  • Содержит ли пользовательский класс (включая возможные добавления к нему) только одну асинхронную операцию, поддерживающую отмену?

  • Могут ли асинхронные операции, поддерживающие отмену, обрабатывать несколько операций ожидания? То есть, принимает ли метод имя_методаAsync параметр userState и поддерживает ли он несколько вызовов до завершения какого-либо из них?

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

Visual Basic

 

Поддерживается несколько одновременных операций

Только одна операция единовременно

Одна асинхронная операция во всем классе

Sub MethodNameAsyncCancel(ByVal userState As Object)
Sub MethodNameAsyncCancel()

Несколько асинхронных операций в классе

Sub CancelAsync(ByVal userState As Object)
Sub CancelAsync()

C#

 

Поддерживается несколько одновременных операций

Только одна операция единовременно

Одна асинхронная операция во всем классе

void MethodNameAsyncCancel(object userState);
void MethodNameAsyncCancel();

Несколько асинхронных операций в классе

void CancelAsync(object userState);
void CancelAsync();

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

Решение об именовании отдельной асинхронной версии операции имя_методаAsyncCancel основывается на возможности более быстрого обнаружения метода в среде разработки, такой как IntelliSense в Visual Studio. Это группирует родственные элементы и отличает их от других элементов, которые никак не связаны с асинхронными функциональными возможностями. Если ожидается добавление дополнительных асинхронных операций в последующих версиях, рекомендуется определить CancelAsync.

Не определяйте несколько методов из приведенной выше таблицы в одном классе. Это будет нелогичным или же заполнит интерфейс класса быстро увеличивающимся количеством методов.

Как правило, эти методы тот же возвращаются, а операция фактически может быть отменена или же нет. В обработчике события имя_методаCompleted объект имя_методаCompletedEventArgs содержит поле Cancelled, который может использоваться клиентами для определения, произошла ли отмена.

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

Дополнительная поддержка свойства IsBusy

Если пользовательский класс не поддерживает несколько одновременных вызовов, рассмотрите возможность предоставления свойства IsBusy. Это позволяет разработчикам определять, выполняется ли метод имя_методаAsync без перехвата исключения из метода имя_методаAsync.

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

Дополнительное предоставление поддержки для отображения хода выполнения

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

  • Дополнительно можно определить событие, которое должно создаваться асинхронной операцией и вызываться для соответствующего потока. Объект ProgressChangedEventArgs содержит индикатор прогресса, основанный на целых числах от 0 до 100.

  • Именование этого события должно происходить следующим образом:

    • ProgressChanged, если в классе имеются несколько асинхронных операций (или в будущих версиях он будет содержать несколько асинхронных операций);

    • MethodNameProgressChanged, если класс содержит одну асинхронную операцию.

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

Это событие должно использовать сигнатуру делегата ProgressChangedEventHandler и класс ProgressChangedEventArgs. С другой стороны, если можно предоставить дополнительные индикаторы хода выполнения, относящиеся к домену (например, количество прочитанных байтов и общее количество байтов в операции загрузки), то следует определить производный класс ProgressChangedEventArgs.

Обратите внимание, что существует только одно событие ProgressChanged или имя_методаProgressChanged для класса независимо от количества поддерживаемых им асинхронных методов. Ожидается, что клиенты будут использовать объект userState, который передается в методы имя_методаAsync для различения обновлений хода выполнения в нескольких одновременных операциях.

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

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

Дополнительное предоставление поддержки для возврата добавочных результатов

Иногда асинхронная операция может возвратить добавочные результаты до завершения. Существует ряд возможностей поддержки этого сценария. Ниже приведены некоторые примеры.

Класс с одной операцией

Если пользовательский класс поддерживает только одну асинхронную операцию и эта операция может возвращать добавочные результаты, выполните следующие действия:

  • Расширьте тип ProgressChangedEventArgs для включения данных добавочных результатов и определите событие имя_методаProgressChanged с помощью этих расширенных данных.

  • Создайте событие имя_методаProgressChanged, если имеется добавочный результат, который следует отобразить.

Это решение применимо специально к классу с одной асинхронной операцией, потому что отсутствуют трудности, связанные с возвращением одним и тем же событием добавочных результатов по "всем операциям", которые возникают при событии имя_методаProgressChanged.

Класс с несколькими операциями, предоставляющий однородные добавочные результаты

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

Придерживайтесь приведенной выше модели для классов с одной операцией, так как та же структура EventArgs будет применима для всех добавочных результатов. Определите событие ProgressChanged вместо события имя_методаProgressChanged, так как оно применимо к нескольким асинхронным методам.

Класс с несколькими операциями, предоставляющий разнородные добавочные результаты

Если пользовательский класс поддерживает несколько асинхронных методов, каждый из которых возвращает данные разных типов, следует выполнить следующие действия:

  • Разделите отчет о добавочных результатах и отчет о ходе выполнения.

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

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

Обработка параметров Out и Ref в методах

Несмотря на то, что в целом использование параметров out и ref не рекомендуется в .NET Framework, ниже приведены правила, которым необходимо следовать, если подобные параметры все-таки присутствуют:

При наличии синхронного метода имя_метода:

  • Параметры out в имя_метода не должны быть частью имя_методаAsync. Напротив, они должны быть частью имя_методаCompletedEventArgs с тем же именем, как и у его параметра-эквивалента в имя_метода (если не существует более подходящего имени).

  • Параметры ref в имя_метода должны отображаться как часть имя_методаAsync и как часть имя_методаCompletedEventArgs с тем же именем, что и у его параметра-эквивалента в имя_метода (если не существует более подходящего имени).

Пример:

Public Function MethodName(ByVal arg1 As String, ByRef arg2 As String, ByRef arg3 As String) As Integer
public int MethodName(string arg1, ref string arg2, out string arg3);

Пользовательский асинхронный метод и его класс AsyncCompletedEventArgs должны выглядеть примерно следующим образом:

Public Sub MethodNameAsync(ByVal arg1 As String, ByVal arg2 As String)

Public Class MethodNameCompletedEventArgs
    Inherits System.ComponentModel.AsyncCompletedEventArgs
    Public ReadOnly Property Result() As Integer 
    End Property
    Public ReadOnly Property Arg2() As String 
    End Property
    Public ReadOnly Property Arg3() As String 
    End Property
End Class
public void MethodNameAsync(string arg1, string arg2);

public class MethodNameCompletedEventArgs : System.ComponentModel.AsyncCompletedEventArgs
{
    public int Result { get; };
    public string Arg2 { get; };
    public string Arg3 { get; };
}

См. также

Задачи

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

Практическое руководство. Фоновое выполнение операции

Практическое руководство. Реализация формы, в которой выполняется фоновая операция

Ссылки

ProgressChangedEventArgs

AsyncCompletedEventArgs

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

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

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

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

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