Обзор асинхронной модели, основанной на событияхEvent-based Asynchronous Pattern Overview

Приложениям, выполняющим множество задач одновременно и при этом активно реагирующим на действия пользователя, часто требуется структура, использующая несколько потоков.Applications that perform many tasks simultaneously, yet remain responsive to user interaction, often require a design that uses multiple threads. Пространство имен System.Threading предоставляет все необходимые средства для создания высокопроизводительных многопоточных приложений, однако для эффективного использования этих средств требуется значительный опыт в области многопоточной программной инженерии.The System.Threading namespace provides all the tools necessary to create high-performance multithreaded applications, but using these tools effectively requires significant experience with multithreaded software engineering. Для относительно простых многопоточных приложений компонент BackgroundWorker предоставляет прямолинейное решение.For relatively simple multithreaded applications, the BackgroundWorker component provides a straightforward solution. Для более сложных асинхронных приложений рекомендуется реализовать класс, который соответствует асинхронной модели на основе событий.For more sophisticated asynchronous applications, consider implementing a class that adheres to the Event-based Asynchronous Pattern.

Асинхронная модель на основе событий позволяет использовать преимущества многопоточных приложений и устраняет многие сложности, присущие многопоточности.The Event-based Asynchronous Pattern makes available the advantages of multithreaded applications while hiding many of the complex issues inherent in multithreaded design. Используя класс, поддерживающий такую модель, вы можете:Using a class that supports this pattern can allow you to:

  • выполнять задачи, занимающие много времени, такие как загрузки и операции с базами данных, "в фоновом режиме" без прерывания работы приложения;Perform time-consuming tasks, such as downloads and database operations, "in the background," without interrupting your application.

  • выполнять несколько операций одновременно, получая уведомления о завершении каждой из них;Execute multiple operations simultaneously, receiving notifications when each completes.

  • ожидать освобождения ресурсов без остановки ("блокирования") приложения;Wait for resources to become available without stopping ("blocking") your application.

  • взаимодействовать с ожидающими асинхронными операциями с использованием привычной модели событий и делегатов.Communicate with pending asynchronous operations using the familiar events-and-delegates model. Дополнительные сведения об использовании обработчиков событий и делегатов см. в статье События.For more information on using event handlers and delegates, see Events.

Класс, который поддерживает асинхронную модель на основе событий, будет иметь один или несколько методов с именем MethodNameAsync.A class that supports the Event-based Asynchronous Pattern will have one or more methods named MethodNameAsync. Эти методы могут копировать синхронные версии, выполняющие ту же операцию в текущем потоке.These methods may mirror synchronous versions, which perform the same operation on the current thread. Этот класс может содержать событие MethodNameCompleted, а также метод MethodNameAsyncCancel (или просто CancelAsync).The class may also have a MethodNameCompleted event and it may have a MethodNameAsyncCancel (or simply CancelAsync) method.

PictureBox — это типичный компонент, поддерживающий асинхронную модель на основе событий.PictureBox is a typical component that supports the Event-based Asynchronous Pattern. Вы можете загрузить изображение синхронно, вызвав его метод Load, но если изображение имеет большой размер или используется медленное подключение к сети, ваше приложение перестанет отвечать до завершения операции загрузки и возврата вызова Load.You can download an image synchronously by calling its Load method, but if the image is large, or if the network connection is slow, your application will stop responding until the download operation is completed and the call to Load returns.

Если вы хотите, чтобы приложение продолжало работать во время загрузки изображения, моно вызвать метод LoadAsync и обработать событие LoadCompleted так, как и любое другое.If you want your application to keep running while the image is loading, you can call the LoadAsync method and handle the LoadCompleted event, just as you would handle any other event. При вызове метода LoadAsync ваше приложение продолжит выполняться, пока загрузка идет в отдельном потоке ("в фоновом режиме").When you call the LoadAsync method, your application will continue to run while the download proceeds on a separate thread ("in the background"). После завершения операции загрузки изображения вызывается ваш обработчик событий, который может изучить параметр AsyncCompletedEventArgs, чтобы определить, была ли загрузка успешной.Your event handler will be called when the image-loading operation is complete, and your event handler can examine the AsyncCompletedEventArgs parameter to determine if the download completed successfully.

Асинхронная модель на основе событий требует наличия возможности отмены асинхронной операции, и элемент управления PictureBox позволяет выполнить это требование с помощью метода CancelAsync.The Event-based Asynchronous Pattern requires that an asynchronous operation can be canceled, and the PictureBox control supports this requirement with its CancelAsync method. При вызове CancelAsync отправляется запрос на остановку ожидающей загрузки, а после отмены задачи возникает событие LoadCompleted.Calling CancelAsync submits a request to stop the pending download, and when the task is canceled, the LoadCompleted event is raised.

Внимание!

Загрузка может закончиться одновременно с получением запроса CancelAsync, поэтому Cancelled может не отражать отмену запроса.It is possible that the download will finish just as the CancelAsync request is made, so Cancelled may not reflect the request to cancel. Это называется состоянием гонки и является распространенной проблемой в многопоточном программировании.This is called a race condition and is a common issue in multithreaded programming. Дополнительные сведения о проблемах многопоточного программирования см. в разделе Рекомендации по работе с потоками.For more information on issues in multithreaded programming, see Managed Threading Best Practices.

Характеристики асинхронной модели на основе событийCharacteristics of the Event-based Asynchronous Pattern

Асинхронная модель на основе событий может принимать разную форму в зависимости от операций, поддерживаемых определенным классом.The Event-based Asynchronous Pattern may take several forms, depending on the complexity of the operations supported by a particular class. Простейшие классы могут иметь один метод MethodNameAsync и одно соответствующее ему событие MethodNameCompleted.The simplest classes may have a single MethodNameAsync method and a corresponding MethodNameCompleted event. Более сложные классы будут содержать несколько методов MethodNameAsync с соответствующими событиями MethodNameCompleted, а также синхронные версии этих методов.More complex classes may have several MethodNameAsync methods, each with a corresponding MethodNameCompleted event, as well as synchronous versions of these methods. Классы также могут поддерживать отмену, составление отчетов о ходе работы и добавочные результаты для каждого асинхронного метода.Classes can optionally support cancellation, progress reporting, and incremental results for each asynchronous method.

Асинхронный метод также поддерживает несколько ожидающих вызовов (несколько одновременно выполняемых вызовов), позволяя вашему коду вызывать его любое число раз, прежде чем он выполнит другие ожидающие операции.An asynchronous method may also support multiple pending calls (multiple concurrent invocations), allowing your code to call it any number of times before it completes other pending operations. Для корректного разрешения данной ситуации вашему приложению может потребоваться отслеживать выполнение каждой операции.Correctly handling this situation may require your application to track the completion of each operation.

Примеры для асинхронной модели на основе событийExamples of the Event-based Asynchronous Pattern

Компоненты SoundPlayer и PictureBox представляют простые реализации асинхронной модели на основе событий.The SoundPlayer and PictureBox components represent simple implementations of the Event-based Asynchronous Pattern. Компоненты WebClient и BackgroundWorker представляют более сложные реализации асинхронной модели на основе событий.The WebClient and BackgroundWorker components represent more complex implementations of the Event-based Asynchronous Pattern.

Ниже приведен пример объявления класса в соответствии с данной моделью:Below is an example class declaration that conforms to the pattern:

Public Class AsyncExample  
    ' Synchronous methods.  
    Public Function Method1(ByVal param As String) As Integer   
    Public Sub Method2(ByVal param As Double)   
  
    ' Asynchronous methods.  
    Overloads Public Sub Method1Async(ByVal param As String)   
    Overloads Public Sub Method1Async(ByVal param As String, ByVal userState As Object)   
    Public Event Method1Completed As Method1CompletedEventHandler  
  
    Overloads Public Sub Method2Async(ByVal param As Double)   
    Overloads Public Sub Method2Async(ByVal param As Double, ByVal userState As Object)   
    Public Event Method2Completed As Method2CompletedEventHandler  
  
    Public Sub CancelAsync(ByVal userState As Object)   
  
    Public ReadOnly Property IsBusy () As Boolean  
  
    ' Class implementation not shown.  
End Class  
public class AsyncExample  
{  
    // Synchronous methods.  
    public int Method1(string param);  
    public void Method2(double param);  
  
    // Asynchronous methods.  
    public void Method1Async(string param);  
    public void Method1Async(string param, object userState);  
    public event Method1CompletedEventHandler Method1Completed;  
  
    public void Method2Async(double param);  
    public void Method2Async(double param, object userState);  
    public event Method2CompletedEventHandler Method2Completed;  
  
    public void CancelAsync(object userState);  
  
    public bool IsBusy { get; }  
  
    // Class implementation not shown.  
}  

Вымышленный класс AsyncExample имеет два метода, которые поддерживают как синхронные, так и асинхронные вызовы.The fictitious AsyncExample class has two methods, both of which support synchronous and asynchronous invocations. Синхронные перегрузки работают аналогично вызову любого метода и выполняют операцию в вызывающем потоке; если выполнение операции требует много времени, перед возвратом вызова может возникнуть значительная задержка.The synchronous overloads behave like any method call and execute the operation on the calling thread; if the operation is time-consuming, there may be a noticeable delay before the call returns. Асинхронные перегрузки запустят операцию в другом потоке и затем немедленно выполнят возврат, позволяя вызывающему потоку продолжить работу, пока операция выполняется "в фоновом режиме".The asynchronous overloads will start the operation on another thread and then return immediately, allowing the calling thread to continue while the operation executes "in the background."

Асинхронные перегрузки методовAsynchronous Method Overloads

Для асинхронных операций существует две перегрузки: с одним вызовом и с несколькими вызовами.There are potentially two overloads for the asynchronous operations: single-invocation and multiple-invocation. Различить две этих формы можно по их сигнатурам методов: форма с несколькими вызовами имеет дополнительный параметр с именем userState.You can distinguish these two forms by their method signatures: the multiple-invocation form has an extra parameter called userState. Эта форма позволяет коду вызвать Method1Async(string param, object userState) несколько раз, не ожидая завершения ожидающих асинхронных операций.This form makes it possible for your code to call Method1Async(string param, object userState) multiple times without waiting for any pending asynchronous operations to finish. С другой стороны, если попытаться вызвать Method1Async(string param) до завершения предыдущего вызова, метод выдает исключение InvalidOperationException.If, on the other hand, you try to call Method1Async(string param) before a previous invocation has completed, the method raises an InvalidOperationException.

Параметр userState для перегрузок с несколькими вызовами позволяет вам различать асинхронные операции.The userState parameter for the multiple-invocation overloads allows you to distinguish among asynchronous operations. Вы указываете уникальное значение (например, GUID или хэш-код) для каждого вызова Method1Async(string param, object userState), что позволяет обработчику событий после завершения каждой из операций определить, какой экземпляр этой операции сообщил о своем завершении.You provide a unique value (for example, a GUID or hash code) for each call to Method1Async(string param, object userState), and when each operation is completed, your event handler can determine which instance of the operation raised the completion event.

Отслеживание ожидающих операцийTracking Pending Operations

Если вы используете перегрузки с несколькими вызовами, вашему коду потребуется отслеживать объекты userState (идентификаторы) для ожидающих задач.If you use the multiple-invocation overloads, your code will need to keep track of the userState objects (task IDs) for pending tasks. Для каждого вызова Method1Async(string param, object userState) обычно создается новый уникальный объект userState, который добавляется в коллекцию.For each call to Method1Async(string param, object userState), you will typically generate a new, unique userState object and add it to a collection. Когда задача, соответствующая этому объекту userState, выдает событие выполнения, реализация метода выполнения проверяет AsyncCompletedEventArgs.UserState и удаляет его из вашей коллекции.When the task corresponding to this userState object raises the completion event, your completion method implementation will examine AsyncCompletedEventArgs.UserState and remove it from your collection. При таком использовании параметр userState играет роль идентификатора задачи.Used this way, the userState parameter takes the role of a task ID.

Примечание

Вам следует внимательно следить за тем, чтобы значение, заданное для userState в ваших вызовах перегрузок с несколькими вызовами, было уникальным.You must be careful to provide a unique value for userState in your calls to multiple-invocation overloads. В случае неуникальных идентификаторов задач асинхронный класс выдает исключение ArgumentException.Non-unique task IDs will cause the asynchronous class throw an ArgumentException.

Отмена ожидающих операцийCanceling Pending Operations

Важно иметь возможность отменить асинхронные операции в любое время до их завершения.It is important to be able to cancel asynchronous operations at any time before their completion. Классы, реализующие асинхронную модель на основе событий, будут иметь метод CancelAsync (если асинхронный метод всего один) или методы MethodNameAsyncCancel (если асинхронных методов несколько).Classes that implement the Event-based Asynchronous Pattern will have a CancelAsync method (if there is only one asynchronous method) or a MethodNameAsyncCancel method (if there are multiple asynchronous methods).

Методы, допускающие несколько вызовов, принимают параметр userState, который можно использовать для отслеживания времени существования каждой задачи.Methods that allow multiple invocations take a userState parameter, which can be used to track the lifetime of each task. CancelAsync принимает параметр userState, позволяющий отменить определенные ожидающие задачи.CancelAsync takes a userState parameter, which allows you to cancel particular pending tasks.

Методы, которые одновременно поддерживают только одну ожидающую операцию, такие как Method1Async(string param), не допускают отмену.Methods that support only a single pending operation at a time, like Method1Async(string param), are not cancelable.

Получение обновлений хода выполнения и добавочных результатовReceiving Progress Updates and Incremental Results

Класс, поддерживающий асинхронную модель на основе событий, может дополнительно предоставлять событие для отслеживания хода выполнения и добавочных результатов.A class that adheres to the Event-based Asynchronous Pattern may optionally provide an event for tracking progress and incremental results. Обычно оно имеет имя ProgressChanged или MethodNameProgressChanged, а соответствующий обработчик событий принимает параметр ProgressChangedEventArgs.This will typically be named ProgressChanged or MethodNameProgressChanged, and its corresponding event handler will take a ProgressChangedEventArgs parameter.

Обработчик событий для события ProgressChanged может проверить свойство ProgressChangedEventArgs.ProgressPercentage, чтобы определить, какой процент асинхронной задачи уже выполнен.The event handler for the ProgressChanged event can examine the ProgressChangedEventArgs.ProgressPercentage property to determine what percentage of an asynchronous task has been completed. Это свойство находится в диапазоне от 0 до 100 и может использоваться для обновления свойства Value элемента ProgressBar.This property will range from 0 to 100, and it can be used to update the Value property of a ProgressBar. Если имеется несколько ожидающих асинхронных операций, вы можете использовать свойство ProgressChangedEventArgs.UserState, чтобы определить, какая операция сообщает о ходе выполнения.If multiple asynchronous operations are pending, you can use the ProgressChangedEventArgs.UserState property to distinguish which operation is reporting progress.

Некоторые классы могут предоставлять добавочные результаты по мере выполнения асинхронных операций.Some classes may report incremental results as asynchronous operations proceed. Эти результаты будут сохранены в классе, который является производным от ProgressChangedEventArgs, и будут иметь вид свойств в производном классе.These results will be stored in a class that derives from ProgressChangedEventArgs and they will appear as properties in the derived class. Вы можете получить доступ к этим результатам в обработчике событий для события ProgressChanged точно так же, как стали бы получать доступ к свойству ProgressPercentage.You can access these results in the event handler for the ProgressChanged event, just as you would access the ProgressPercentage property. Если имеется несколько ожидающих асинхронных операций, вы можете использовать свойство UserState, чтобы определить, какая операция выдает добавочные результаты.If multiple asynchronous operations are pending, you can use the UserState property to distinguish which operation is reporting incremental results.

См. такжеSee also