Vue d'ensemble du modèle asynchrone basé sur des événements

Mise à jour : novembre 2007

Les applications qui effectuent de nombreuses tâches simultanément tout en restant réactives à l'interaction avec l'utilisateur nécessitent souvent une conception utilisant plusieurs threads. L'espace de noms System.Threading fournit tous les outils nécessaires à la création d'applications multithread haute performance, mais l'utilisation efficace de ces outils exige une connaissance approfondie de l'ingénierie logicielle multithread. Pour les applications multithread relativement simples, le composant BackgroundWorker fournit une solution simple. Pour les applications asynchrones plus sophistiquées, envisagez l'implémentation d'une classe obéissant au modèle asynchrone basé sur des événements.

Le modèle asynchrone basé sur des événements permet de profiter des avantages des applications multithread tout en masquant de nombreux problèmes complexes inhérents à la conception multithread. L'utilisation d'une classe prenant en charge ce modèle peut vous permettre :

  • d'effectuer, « en arrière-plan », des tâches de longue durée, telles que des téléchargements et des opérations de base de données, sans interrompre votre application ;

  • d'exécuter plusieurs opérations simultanément, en recevant des notifications lorsque chacune d'elles se termine ;

  • d'attendre que les ressources soient disponibles sans arrêter (« bloquer ») votre application ;

  • de communiquer avec les opérations asynchrones en attente à l'aide du modèle d'événements et de délégués connu. Pour plus d'informations sur l'utilisation des gestionnaires d'événements et des délégués, consultez Événements et délégués.

Une classe prenant en charge le modèle asynchrone basé sur des événements possédera une ou plusieurs méthodes nommées NomMéthodeAsync. Ces méthodes peuvent refléter des versions synchrones qui exécutent la même opération sur le thread courant. La classe peut également posséder un événement NomMéthodeCompleted et une méthode NomMéthodeAsyncCancel (ou simplement CancelAsync).

PictureBox est un composant courant qui prend en charge le modèle asynchrone basé sur des événements Vous pouvez télécharger une image de façon synchrone en appelant sa méthode Load, mais si l'image est grande ou si la connexion réseau est lente, votre application s'arrêtera (« se bloquera ») jusqu'à ce que l'opération de téléchargement soit terminée et que l'appel à Load soit retourné.

Si vous souhaitez que votre application continue de s'exécuter pendant le chargement de l'image, vous pouvez appeler la méthode LoadAsync et gérer l'événement LoadCompleted, tout comme vous le feriez pour tout autre événement. Lorsque vous appelez la méthode LoadAsync, l'exécution de votre application se poursuit pendant que le téléchargement s'effectue sur un thread séparé (« en arrière-plan »). Votre gestionnaire d'événements est appelé lorsque l'opération de chargement d'image est terminée, et peut examiner le paramètre AsyncCompletedEventArgs pour déterminer si le téléchargement s'est déroulé correctement.

Le modèle asynchrone basé sur des événements nécessite qu'une opération asynchrone puisse être annulée ; le contrôle PictureBox prend en charge cette exigence avec sa méthode CancelAsync. L'appel de CancelAsync soumet une demande d'arrêt du téléchargement en attente et, lorsque la tâche est annulée, l'événement LoadCompleted est déclenché.

Attention :

Il est possible que le téléchargement se termine au moment où la demande CancelAsync est faite, de sorte que Cancelled ne peut pas refléter la demande d'annulation. Il s'agit d'une condition de concurrence critique qui est un problème courant dans la programmation multithread. Pour plus d'informations sur les problèmes relatifs à la programmation multithread, consultez Méthodes conseillées pour le threading managé.

Caractéristiques du modèle asynchrone basé sur des événements

Le modèle asynchrone basé sur des événements peut prendre plusieurs formes selon la complexité des opérations prises en charge par une classe particulière. Les classes les plus simples peuvent avoir une seule méthode MethodNameAsync et un événement MethodNameCompleted correspondant. Les classes plus complexes peuvent avoir plusieurs méthodes MethodNameAsync, chacune avec un événement MethodNameCompleted correspondant, ainsi que des versions synchrones de ces méthodes. Les classes peuvent éventuellement prendre en charge l'annulation, le rapport de progression et les résultats incrémentiels pour chaque méthode asynchrone.

Une méthode asynchrone peut également prendre en charge plusieurs appels en attente (plusieurs appels simultanés), ce qui permet à votre code de l'appeler autant de fois que nécessaire avant de terminer d'autres opérations en attente. La gestion correcte de cette situation peut nécessiter que votre application effectue le suivi de l'achèvement de chaque opération.

Exemples du modèle asynchrone basé sur des événements

Les composants SoundPlayer et PictureBox représentent des implémentations simples du modèle asynchrone basé sur des événements. Les composants WebClient et BackgroundWorker représentent des implémentations plus complexes du modèle asynchrone basé sur des événements.

Voici un exemple de déclaration de classe conforme au modèle :

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.
}

La classe AsyncExample fictive possède deux méthodes qui prennent en charge les appels synchrones et asynchrones. Les surcharges synchrones se comportent comme tout appel de méthode et exécutent l'opération sur le thread appelant ; si l'opération est de longue durée, le délai de retour de l'appel peut être important. Les surcharges asynchrones démarreront l'opération sur un autre thread, puis elles seront retournées immédiatement, permettant au thread appelant de continuer pendant l'exécution de l'opération « en arrière-plan ».

Surcharges de méthode asynchrone

Il existe potentiellement deux surcharges pour les opérations asynchrones : l'appel unique et les appels multiples. Ces deux surcharges se distinguent par leurs signatures de méthode : la surcharge d'appels multiples possède un paramètre supplémentaire appelé userState. Elle permet à votre code d'appeler Method1Async(string param, object userState) plusieurs fois sans attendre que les opérations asynchrones en attente se terminent. En revanche, si vous tentez d'appeler Method1Async(string param) avant qu'un appel antérieur soit terminé, la méthode lève une InvalidOperationException.

Le paramètre userState pour les surcharges d'appels multiples vous permet de différencier les opérations asynchrones. Vous fournissez une valeur unique (par exemple, un GUID ou un code de hachage) pour chaque appel à Method1Async(string param, object userState), et lorsque chaque opération est terminée, votre gestionnaire d'événements peut déterminer l'instance de l'opération qui a déclenché l'événement d'achèvement.

Suivi des opérations en attente

Si vous utilisez les surcharges d'appels multiples, votre code devra conserver une trace des objets userState (ID de tâche) pour les tâches en attente. Pour chaque appel à Method1Async(string param, object userState), vous générez normalement un nouvel objet userState unique et l'ajoutez à une collection. Lorsque la tâche correspondant à cet objet userState déclenche l'événement d'achèvement, l'implémentation de votre méthode d'achèvement examine AsyncCompletedEventArgs.UserState et le supprime de votre collection. Utilisé de cette manière, le paramètre userState joue le rôle d'un ID de tâche.

Remarque :

Vous devez fournir une valeur unique pour userState dans vos appels aux surcharges d'appels multiples. Les ID de tâche non uniques conduiront la classe asynchrone à lever une ArgumentException.

Annulation des opérations en attente

Il est important de pouvoir annuler des opérations asynchrones à tout moment avant leur achèvement. Les classes qui implémentent le modèle asynchrone basé sur des événements auront une méthode CancelAsync (s'il existe plusieurs méthodes asynchrones) ou une méthode NomMéthodeAsyncCancel (s'il existe une seule méthode asynchrone).

Les méthodes qui autorisent les appels multiples acceptent un paramètre userState qui peut être utilisé pour assurer le suivi de la durée de vie de chaque tâche. CancelAsync accepte un paramètre userState qui vous permet d'annuler des tâches en attente particulières.

Les méthodes qui prennent en charge une seule opération en attente à la fois, comme Method1Async(string param), ne sont pas annulables.

Réception des mises à jour de progression et des résultats incrémentiels

Une classe obéissant au modèle asynchrone basé sur des événements peut éventuellement fournir un événement pour assurer le suivi de la progression et des résultats incrémentiels. Celui-ci sera généralement appelé ProgressChanged ou NomMéthodeProgressChanged et son gestionnaire d'événements correspondant acceptera un paramètre ProgressChangedEventArgs.

Le gestionnaire d'événements pour l'événement ProgressChanged peut examiner la propriété ProgressChangedEventArgs.ProgressPercentage afin de déterminer le pourcentage d'exécution d'une tâche asynchrone. Cette propriété sera comprise entre 0 et 100 et pourra être utilisée pour mettre à jour la propriété Value d'une ProgressBar. Si plusieurs opérations asynchrones sont en attente, vous pouvez utiliser la propriété ProgressChangedEventArgs.UserState pour distinguer l'opération qui signale la progression.

Certaines classes peuvent signaler des résultats incrémentiels au fur et à mesure que les opérations asynchrones sont exécutées. Ces résultats seront stockés dans une classe dérivant de ProgressChangedEventArgs et apparaîtront sous forme de propriétés dans la classe dérivée. Vous pouvez accéder à ces résultats dans le gestionnaire d'événements pour l'événement ProgressChanged, tout comme vous accéderiez à la propriété ProgressPercentage. Si plusieurs opérations asynchrones sont en attente, vous pouvez utiliser la propriété UserState pour distinguer l'opération qui signale des résultats incrémentiels.

Voir aussi

Tâches

Comment : utiliser des composants qui prennent en charge le modèle asynchrone basé sur des événements

Comment : exécuter une opération en arrière-plan

Comment : implémenter un formulaire qui utilise une opération d'arrière-plan

Concepts

Méthodes conseillées pour implémenter le modèle asynchrone basé sur des événements

Choix du moment auquel implémenter le modèle asynchrone basé sur les événements

Référence

ProgressChangedEventArgs

BackgroundWorker

AsyncCompletedEventArgs

Autres ressources

Programmation multithread avec le modèle asynchrone basé sur les événements