Méthodes conseillées pour implémenter le modèle asynchrone basé sur des événements
Mise à jour : novembre 2007
Le modèle asynchrone basé sur des événements vous fournit une méthode efficace pour exposer un comportement asynchrone dans les classes, avec une sémantique de délégué et d'événement connue. Pour implémenter le modèle asynchrone basé sur des événements, vous devez suivre des spécifications comportementales spécifiques. Les sections suivantes décrivent les spécifications et les indications que vous devez prendre en compte lors de l'implémentation d'une classe qui suit le modèle asynchrone basé sur des événements.
Pour une vue d'ensemble, consultez Implémentation du modèle asynchrone basé sur des événements.
La liste suivante présente les meilleures pratiques décrites dans cette rubrique :
Garanties comportementales requises
Achèvement
Événement terminé et EventArgs
Exécution simultanée d'opérations
Accès aux résultats
Rapport de progression
Implémentation de IsBusy
Annulation
Erreurs et exceptions
Threads et contextes
Indications
Garanties comportementales requises
Si vous implémentez le modèle asynchrone basé sur des événements, vous devez fournir plusieurs garanties pour certifier que votre classe se comportera correctement et que les clients de votre classe peuvent se fier à un tel comportement.
Achèvement
Appelez toujours le gestionnaire d'événements MethodNameCompleted dans le cas d'un achèvement correct, d'une erreur ou d'une annulation. Les applications ne doivent jamais se trouver dans une situation où elles restent inactives et où l'achèvement n'a jamais lieu. Le cas où l'opération asynchrone elle-même est conçue de façon à ne jamais se terminer fait exception à cette règle.
Événement terminé et EventArgs
Pour chaque méthode MethodNameAsync séparée, appliquez les impératifs de design suivants :
Définissez un événement MethodNameCompleted sur la même classe que la méthode.
Définissez une classe EventArgs et le délégué qui l'accompagne pour l'événement MethodNameCompleted dérivant de la classe AsyncCompletedEventArgs. Le nom de classe par défaut doit avoir le format MethodNameCompletedEventArgs.
Assurez-vous que la classe EventArgs est spécifique aux valeurs de retour de la méthode MethodName. Lorsque vous utilisez la classe EventArgs, vous ne devez jamais attendre des développeurs qu'ils effectuent un cast du résultat.
L'exemple de code suivant illustre respectivement une bonne et une mauvaise implémentation de cet impératif de design.
[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);
}
Exécution simultanée d'opérations
Si votre classe prend en charge plusieurs appels simultanés, autorisez le développeur à effectuer le suivi de chaque appel séparément en définissant la surcharge MethodNameAsync qui accepte un paramètre d'état de valeurs d'objet, ou l'ID de la tâche, appelé userSuppliedState. Ce paramètre doit toujours être le dernier paramètre dans la signature de la méthode MethodNameAsync.
Si votre classe définit la surcharge MethodNameAsync qui accepte un paramètre d'état de valeurs d'objet ou l'ID de la tâche, vous devez assurer le suivi de la durée de vie de l'opération avec cet ID de tâche et le restituer au gestionnaire d'achèvement. Des classes d'assistance sont à votre disposition. Pour plus d'informations sur la gestion de l'accès concurrentiel, consultez Procédure pas à pas : implémentation d'un composant qui prend en charge le modèle asynchrone basé sur des événements.
Si votre classe définit la méthode MethodNameAsync sans le paramètre d'état et qu'elle ne prend pas en charge plusieurs appels simultanés, assurez-vous que toute tentative d'appel de MethodNameAsync avant la fin de l'appel précédent de MethodNameAsync lève une InvalidOperationException.
En général, ne levez pas d'exception si la méthode MethodNameAsync sans le paramètre userSuppliedState est appelée plusieurs fois de sorte que plusieurs opérations sont en attente. Vous pouvez lever une exception lorsque votre classe ne peut pas régler explicitement cette situation, mais vous supposez que les développeurs peuvent gérer ces multiples rappels impossibles à distinguer.
Accès aux résultats
Si une erreur s'est produite pendant l'exécution de l'opération asynchrone, les résultats ne sont pas accessibles. Assurez-vous que l'accès à une propriété dans AsyncCompletedEventArgs lorsque Error n'est pas null lève l'exception référencée par Error. La classe AsyncCompletedEventArgs fournit la méthode RaiseExceptionIfNecessary à cet effet.
Assurez-vous que toute tentative d'accès au résultat lève une InvalidOperationException indiquant que l'opération a été annulée. Utilisez la méthode AsyncCompletedEventArgs.RaiseExceptionIfNecessary pour effectuer cette vérification.
Rapport de progression
Si possible, prenez en charge le rapport de progression. Cela permet aux développeurs de fournir une meilleure expérience aux utilisateurs de l'application lorsqu'ils utilisent votre classe.
Si vous implémentez un événement ProgressChanged/MethodNameProgressChanged, assurez-vous qu'aucun événement de ce type n'est déclenché pour une opération asynchrone spécifique après le déclenchement de l'événement MethodNameCompleted de cette opération.
Si le ProgressChangedEventArgs standard est rempli, assurez-vous que le ProgressPercentage peut toujours être interprété comme un pourcentage. Le pourcentage n'a pas besoin d'être exact mais il doit représenter un pourcentage. Si la mesure de votre rapport de progression ne doit pas être exprimée en pourcentage, dérivez une classe de la classe ProgressChangedEventArgs et conservez la valeur 0 pour ProgressPercentage. Évitez d'utiliser une mesure de rapport autre qu'un pourcentage.
Assurez-vous que l'événement ProgressChanged est déclenché sur le thread approprié et au moment approprié du cycle de vie de l'application. Pour plus d'informations, consultez la section Threads et contextes.
Implémentation de IsBusy
N'exposez pas une propriété IsBusy si votre classe prend en charge plusieurs appels simultanés. Par exemple, les proxies de service Web XML n'exposent pas de propriété IsBusy car ils prennent en charge plusieurs appels simultanés de méthodes asynchrones.
La propriété IsBusy doit retourner la valeur true après l'appel de la méthode MethodNameAsync et avant que l'événement MethodNameCompleted ne soit déclenché. Sinon, elle doit retourner la valeur false. Les composants BackgroundWorker et WebClient sont des exemples de classes qui exposent une propriété IsBusy.
Annulation
Si possible, prenez en charge l'annulation. Cela permet aux développeurs de fournir une meilleure expérience aux utilisateurs de l'application lorsqu'ils utilisent votre classe.
En cas d'annulation, définissez l'indicateur Cancelled dans l'objet AsyncCompletedEventArgs.
Assurez-vous que toute tentative d'accès au résultat déclenche une InvalidOperationException indiquant que l'opération a été annulée. Utilisez la méthode AsyncCompletedEventArgs.RaiseExceptionIfNecessary pour effectuer cette vérification.
Assurez-vous que les appels à une méthode d'annulation sont toujours retournés avec succès et qu'ils ne lèvent jamais d'exception. En général, un client n'est pas averti de la possibilité d'annuler une opération à un moment donné ou de l'aboutissement d'une annulation précédemment émise. Toutefois, l'application est toujours informée lorsqu'une annulation aboutit car l'application participe à l'état d'achèvement.
Déclenchez l'événement MethodNameCompleted lorsque l'opération est annulée.
Erreurs et exceptions
- Interceptez toutes les exceptions qui se produisent dans l'opération asynchrone et affectez la valeur de la propriété AsyncCompletedEventArgs.Error à cette exception.
Threads et contextes
Pour un fonctionnement correct de votre classe, il est essentiel que les gestionnaires d'événements du client soient appelés sur le thread ou le contexte approprié pour le modèle d'application donné, y compris les applications ASP.NET et Windows Forms. Deux classes d'assistance importantes sont fournies pour garantir que votre classe asynchrone se comporte correctement sous tout modèle d'application : AsyncOperation et AsyncOperationManager.
AsyncOperationManager fournit une méthode, CreateOperation, qui retourne une AsyncOperation. Votre méthode MethodNameAsync appelle CreateOperation et votre classe utilise l'AsyncOperation retournée pour assurer le suivi de la durée de vie de la tâche asynchrone.
Pour signaler la progression, les résultats incrémentiels et l'achèvement au client, appelez les méthodes Post et OperationCompleted sur AsyncOperation. AsyncOperation est chargé de marshaler les appels aux gestionnaires d'événements du client au thread ou contexte approprié.
Remarque : |
---|
Vous pouvez contourner ces règles si vous souhaitez explicitement aller à l'encontre de la stratégie du modèle d'application mais que vous souhaitez toujours profiter des autres avantages de l'utilisation du modèle asynchrone basé sur des événements. Par exemple, vous souhaitez peut-être qu'une classe fonctionnant dans les Windows Forms soit libre de thread. Vous pouvez créer une classe libre de thread tant que les développeurs comprennent les restrictions impliquées. Les applications console ne synchronisent pas l'exécution des appels Post. Cela peut provoquer le déclenchement des événements ProgressChanged de manière désordonnée. Si vous souhaitez que l'exécution des appels Post soit sérialisée, implémentez et installez une classe System.Threading.SynchronizationContext. |
Pour plus d'informations sur l'utilisation de AsyncOperation et AsyncOperationManager pour activer vos opérations asynchrones, consultez Procédure pas à pas : implémentation d'un composant qui prend en charge le modèle asynchrone basé sur des événements.
Indications
Dans l'idéal, chaque appel de méthode doit être indépendant des autres. Vous devez éviter de coupler des appels à des ressources partagées. Si les ressources doivent être partagées par les appels, vous devrez fournir un mécanisme de synchronisation approprié dans votre implémentation.
Les designs qui demandent au client d'implémenter la synchronisation sont déconseillés. Par exemple, vous pouvez avoir une méthode asynchrone qui reçoit un objet statique global comme paramètre ; plusieurs appels simultanés d'une telle méthode peuvent entraîner l'altération des données ou des blocages.
Si vous implémentez une méthode avec la surcharge d'appels multiples (userState dans la signature), votre classe devra gérer une collection d'états utilisateur, ou d'ID de tâche, et leurs opérations correspondantes en attente. Cette collection doit être protégée par des zones lock car les divers appels ajoutent et suppriment des objets userState dans la collection.
Envisagez la réutilisation des classes CompletedEventArgs lorsque cela est possible et approprié. Dans ce cas, l'affectation de nom n'est pas cohérente avec le nom de la méthode car un délégué et un type EventArgs donnés ne sont pas liés à une seule méthode. Toutefois, il n'est pas acceptable d'obliger les développeurs à effectuer un cast de la valeur récupérée d'une propriété sur EventArgs.
Si vous créez une classe dérivée de Component, n'implémentez et n'installez pas votre propre classe SynchronizationContext. Les modèles d'application, et non les composants, contrôlent le SynchronizationContext qui est utilisé.
Lorsque vous utilisez le multithreading de tout type, vous vous exposez potentiellement à des bogues très graves et complexes. Avant d'implémenter une solution utilisant le multithreading, consultez Méthodes conseillées pour le threading managé.
Voir aussi
Tâches
Concepts
Implémentation du modèle asynchrone basé sur des événements
Choix du moment auquel implémenter le modèle asynchrone basé sur les événements
Méthodes conseillées pour implémenter le modèle asynchrone basé sur des événements
Référence
Autres ressources
Programmation multithread avec le modèle asynchrone basé sur les événements