Partager via


Modèle de programmation asynchrone des tâches

Vous pouvez éviter les goulots d’étranglement des performances et améliorer la réactivité globale de votre application à l’aide de la programmation asynchrone. Toutefois, les techniques traditionnelles d’écriture d’applications asynchrones peuvent être compliquées, ce qui les rend difficiles à écrire, déboguer et gérer.

C# prend en charge l’approche simplifiée, la programmation asynchrone, qui tire parti de la prise en charge asynchrone dans le runtime .NET. Le compilateur effectue le travail difficile que le développeur a utilisé pour effectuer, et votre application conserve une structure logique qui ressemble à du code synchrone. Par conséquent, vous bénéficiez de tous les avantages de la programmation asynchrone avec une fraction de l’effort.

Cette rubrique fournit une vue d’ensemble du moment et de la façon d’utiliser la programmation asynchrone et inclut des liens vers des rubriques de support qui contiennent des détails et des exemples.

Async améliore la réactivité

La synchronisation est essentielle pour les activités potentiellement bloquantes, telles que l’accès web. L’accès à une ressource web est parfois lent ou retardé. Si une telle activité est bloquée dans un processus synchrone, l’application entière doit attendre. Dans un processus asynchrone, l’application peut continuer avec d’autres travaux qui ne dépendent pas de la ressource web tant que la tâche potentiellement bloquante n’est pas terminée.

Le tableau suivant présente les zones typiques où la programmation asynchrone améliore la réactivité. Les API répertoriées à partir de .NET et windows Runtime contiennent des méthodes qui prennent en charge la programmation asynchrone.

Domaine d'application Types .NET avec méthodes asynchrones Types Windows Runtime avec méthodes asynchrones
Accès web HttpClient Windows.Web.Http.HttpClient
SyndicationClient
Utilisation de fichiers JsonSerializer
StreamReader
StreamWriter
XmlReader
XmlWriter
StorageFile
Utilisation d’images MediaCapture
BitmapEncoder
BitmapDecoder
Programmation WCF Opérations synchrones et asynchrones

Asynchrony prouve particulièrement utile pour les applications qui accèdent au thread d’interface utilisateur, car toutes les activités liées à l’interface utilisateur partagent généralement un thread. Si un processus est bloqué dans une application synchrone, tous sont bloqués. Votre application ne répond plus et vous pouvez conclure qu'elle a rencontré une défaillance, alors qu'elle attend simplement.

Lorsque vous utilisez des méthodes asynchrones, l’application continue de répondre à l’interface utilisateur. Vous pouvez redimensionner ou réduire une fenêtre, par exemple, ou fermer l’application si vous ne souhaitez pas attendre qu’elle se termine.

L’approche asynchrone ajoute l’équivalent d’une transmission automatique à la liste des options que vous pouvez choisir lors de la conception d’opérations asynchrones. Autrement dit, vous bénéficiez de tous les avantages de la programmation asynchrone traditionnelle, mais avec beaucoup moins d’efforts du développeur.

Les méthodes asynchrones sont faciles à écrire

Les mots clés asynchrones et await en C# constituent le cœur de la programmation asynchrone. En utilisant ces deux mots clés, vous pouvez utiliser des ressources dans .NET Framework, .NET Core ou Windows Runtime pour créer une méthode asynchrone presque aussi facilement que vous créez une méthode synchrone. Les méthodes asynchrones que vous définissez à l’aide du async mot clé sont appelées méthodes asynchrones.

L’exemple suivant montre une méthode asynchrone. Presque tout dans le code doit vous sembler familier.

Vous trouverez un exemple complet de Windows Presentation Foundation (WPF) disponible en téléchargement à partir de Programmation asynchrone avec async et await en C#.

public async Task<int> GetUrlContentLengthAsync()
{
    using var client = new HttpClient();

    Task<string> getStringTask =
        client.GetStringAsync("https://learn.microsoft.com/dotnet");

    DoIndependentWork();

    string contents = await getStringTask;

    return contents.Length;
}

void DoIndependentWork()
{
    Console.WriteLine("Working...");
}

Vous pouvez apprendre plusieurs pratiques de l’exemple précédent. Commencez par la signature de méthode. Il inclut le async modificateur. Le type de retour est Task<int> (voir la section « Types de retour » pour plus d’options). Le nom de la méthode se termine par Async. Dans le corps de la méthode, GetStringAsync retourne un Task<string>. Cela signifie que quand vous attendez (await) la tâche, vous obtenez un string (contents). Avant d’attendre la tâche, vous pouvez effectuer tout travail qui ne repose pas sur le string de GetStringAsync.

Faites particulièrement attention à l’opérateur await. Elle suspend GetUrlContentLengthAsync:

  • GetUrlContentLengthAsync ne peut pas continuer tant que getStringTask n’est pas terminé.
  • Pendant ce temps, le contrôle revient à l’appelant de GetUrlContentLengthAsync.
  • Le contrôle reprend ici une fois getStringTask terminé.
  • L’opérateur await récupère ensuite le string résultat à partir de getStringTask.

L’instruction return spécifie un résultat entier. Toutes les méthodes qui attendent GetUrlContentLengthAsync vont récupérer la valeur de longueur.

Si GetUrlContentLengthAsync n'a aucun travail qu'il peut effectuer entre l'appel de GetStringAsync et l'attente de son achèvement, vous pouvez simplifier votre code en appelant et attendant dans l'instruction unique suivante.

string contents = await client.GetStringAsync("https://learn.microsoft.com/dotnet");

Les caractéristiques suivantes résument ce qui rend l’exemple précédent d’une méthode asynchrone :

  • La signature de méthode inclut un async modificateur.

  • Le nom d’une méthode asynchrone, par convention, se termine par un suffixe « Async ».

  • Le type de retour est l’un des types suivants :

    • Task<TResult> si votre méthode a une instruction return dans laquelle l’opérande a le type TResult.
    • Task si votre méthode n’a pas d’instruction return ou n’a pas d’instruction return sans opérande.
    • void si vous écrivez un gestionnaire d’événements asynchrone.
    • Tout autre type qui a une GetAwaiter méthode.

    Pour plus d’informations, consultez la section Types de retour et paramètres .

  • La méthode inclut généralement au moins une await expression, qui marque un point où la méthode ne peut pas continuer tant que l’opération asynchrone attendue n’est pas terminée. En attendant, la méthode est suspendue et le contrôle retourne à l’appelant de la méthode. La section suivante de cette rubrique illustre ce qui se passe au point de suspension.

Dans les méthodes asynchrones, vous utilisez les mots clés et types fournis pour indiquer ce que vous souhaitez faire, et le compilateur effectue le reste, y compris suivre ce qui doit se produire lorsque le contrôle retourne à un point d’attente dans une méthode suspendue. Certains processus de routine, tels que les boucles et la gestion des exceptions, peuvent être difficiles à gérer dans le code asynchrone traditionnel. Dans une méthode asynchrone, vous écrivez ces éléments autant que vous le feriez dans une solution synchrone, et le problème est résolu.

Pour plus d’informations sur l’asynchronie dans les versions précédentes de .NET Framework, consultez la programmation asynchrone TPL et .NET Framework traditionnelle.

Que se passe-t-il dans une méthode asynchrone

La chose la plus importante à comprendre dans la programmation asynchrone est la façon dont le flux de contrôle passe de la méthode à la méthode. Le diagramme suivant vous guide tout au long du processus :

Navigation de suivi d’un flux de contrôle asynchrone

Les nombres du diagramme correspondent aux étapes suivantes, initiés lorsqu’une méthode appelante appelle la méthode asynchrone.

  1. Une méthode appelante appelle la méthode GetUrlContentLengthAsync asynchrone et l'attend.

  2. GetUrlContentLengthAsync crée une HttpClient instance et appelle la GetStringAsync méthode asynchrone pour télécharger le contenu d’un site web sous forme de chaîne.

  3. Quelque chose se produit dans GetStringAsync ce qui suspend sa progression. Elle peut être obligée d'attendre la fin d'un téléchargement sur un site Web ou de toute autre activité bloquante. Pour éviter de bloquer les ressources, GetStringAsync donne le contrôle à son appelant. GetUrlContentLengthAsync

    GetStringAsync retourne un Task<TResult>, où TResult est une chaîne et GetUrlContentLengthAsync affecte la tâche à la getStringTask variable. La tâche représente le processus en cours de l'appel de GetStringAsync, avec l'engagement de produire une valeur de chaîne réelle lorsque le travail est terminé.

  4. Comme getStringTask n’a pas encore été attendu, GetUrlContentLengthAsync peut continuer avec d’autres travaux qui ne dépendent pas du résultat final de GetStringAsync. Ce travail est représenté par un appel à la méthode DoIndependentWorksynchrone.

  5. DoIndependentWork est une méthode synchrone qui effectue son travail et retourne à son appelant.

  6. GetUrlContentLengthAsync n’a plus de travail à exécuter sans un résultat de getStringTask. GetUrlContentLengthAsync Souhaite ensuite calculer et retourner la longueur de la chaîne téléchargée, mais la méthode ne peut pas calculer cette valeur tant que la méthode n’a pas la chaîne.

    Par conséquent, GetUrlContentLengthAsync utilise un opérateur await pour suspendre sa progression et pour donner le contrôle à la méthode appelée GetUrlContentLengthAsync. GetUrlContentLengthAsync retourne un Task<int> à l’appelant. La tâche représente une promesse de produire un résultat entier qui est la longueur de la chaîne téléchargée.

    Remarque

    Si GetStringAsync (et par conséquent getStringTask) se termine avant que GetUrlContentLengthAsync ne l'attende, le contrôle reste en GetUrlContentLengthAsync. Les efforts de la suspension puis du retour à GetUrlContentLengthAsync seraient gaspillés si le processus asynchrone getStringTask appelé a déjà été terminé et que GetUrlContentLengthAsync n'a pas besoin d'attendre le résultat final.

    À l’intérieur de la méthode appelante, le modèle de traitement continue. L’appelant peut effectuer d’autres tâches qui ne dépendent pas du résultat de GetUrlContentLengthAsync avant d’attendre ce résultat, ou bien il peut attendre immédiatement. La méthode appelante attend GetUrlContentLengthAsync, et GetUrlContentLengthAsync attend GetStringAsync.

  7. GetStringAsync se termine et génère un résultat de chaîne. Le résultat de chaîne n'est pas retourné par l'appel de GetStringAsync de la façon à laquelle vous pourriez vous attendre. (N’oubliez pas que la méthode a déjà retourné une tâche à l’étape 3.) Au lieu de cela, le résultat de la chaîne est stocké dans la tâche qui représente l’achèvement de la méthode. getStringTask L’opérateur await récupère le résultat à partir de getStringTask. L’instruction d’affectation affecte le résultat récupéré à contents.

  8. Quand GetUrlContentLengthAsync a le résultat sous forme de chaîne, la méthode peut calculer la longueur de cette chaîne. Le travail de GetUrlContentLengthAsync est également terminé, et ensuite le gestionnaire d’événements en attente peut reprendre. Dans l’exemple complet à la fin de la rubrique, vous pouvez confirmer que le gestionnaire d’événements récupère et imprime la valeur du résultat de longueur. Si vous débutez avec la programmation asynchrone, prenez une minute pour prendre en compte la différence entre le comportement synchrone et asynchrone. Une méthode synchrone retourne une fois son travail terminé (étape 5), mais une méthode asynchrone retourne une valeur de tâche lorsque son travail est suspendu (étapes 3 et 6). Lorsque la méthode asynchrone termine son travail, la tâche est marquée comme terminée et le résultat, le cas échéant, est stocké dans la tâche.

Méthodes asynchrones d’API

Vous vous demandez peut-être où trouver des méthodes telles que GetStringAsync celles qui prennent en charge la programmation asynchrone. .NET Framework 4.5 ou version ultérieure et .NET Core contiennent de nombreux membres qui fonctionnent avec async et await. Vous pouvez les reconnaître par le suffixe « Async » ajouté au nom du membre, et par leur type de retour de Task ou Task<TResult>. Par exemple, la System.IO.Stream classe contient des méthodes telles que CopyToAsync, ReadAsyncet WriteAsync en même temps que les méthodes CopyTosynchrones , Readet Write.

Windows Runtime contient également de nombreuses méthodes que vous pouvez utiliser avec async et await dans les applications Windows. Pour plus d’informations, consultez Threading et programmation asynchrone pour le développement UWP et la programmation asynchrone (applications du Windows Store) et démarrage rapide : Appel d’API asynchrones en C# ou Visual Basic si vous utilisez des versions antérieures de Windows Runtime.

Fils de discussion

Les méthodes asynchrones sont destinées à être des opérations non bloquantes. Une await expression dans une méthode asynchrone ne bloque pas le thread actuel pendant l’exécution de la tâche attendue. Au lieu de cela, l'expression inscrit le reste de la méthode comme continuation et retourne le contrôle à l'appelant de la méthode async.

Les async mots clés et await ne provoquent pas la création de threads supplémentaires. Les méthodes asynchrones ne nécessitent pas de multithreading, car une méthode asynchrone ne s’exécute pas sur son propre thread. La méthode s’exécute sur le contexte de synchronisation actuel et utilise le temps sur le thread uniquement lorsque la méthode est active. Vous pouvez utiliser Task.Run pour déplacer le travail lié au processeur vers un thread d’arrière-plan, mais un thread d’arrière-plan n’aide pas avec un processus qui attend simplement que les résultats soient disponibles.

L’approche basée sur async pour la programmation asynchrone est préférable aux approches existantes dans la plupart des cas. En particulier, cette approche est préférable à la classe BackgroundWorker pour les opérations utilisant de nombreuses E/S, car le code est plus simple et il est inutile de vous protéger contre des conditions de concurrence. Avec la méthode Task.Run, la programmation asynchrone est meilleure que BackgroundWorker pour les opérations gourmandes en processeur, car elle sépare les détails de coordination de l'exécution de votre code du travail que Task.Run transfère au pool de threads.

async et await

Si vous spécifiez qu’une méthode est une méthode asynchrone à l’aide du modificateur asynchrone , vous activez les deux fonctionnalités suivantes.

  • La méthode asynchrone marquée peut utiliser await pour désigner des points de suspension. L’opérateur await indique au compilateur que la méthode asynchrone ne peut pas continuer après ce point tant que le processus asynchrone attendu n’est pas terminé. En attendant, le contrôle retourne à l’appelant de la méthode asynchrone.

    La suspension d’une méthode asynchrone à une await expression ne constitue pas une sortie de la méthode et finally les blocs ne s’exécutent pas.

  • La méthode async marquée peut elle-même être attendue par les méthodes qui l'appellent.

Une méthode asynchrone contient généralement une ou plusieurs occurrences d’un await opérateur, mais l’absence d’expressions await ne provoque pas d’erreur du compilateur. Si une méthode asynchrone n’utilise pas d’opérateur await pour marquer un point de suspension, la méthode s’exécute comme une méthode synchrone, malgré le async modificateur. Le compilateur émet un avertissement pour ces méthodes.

async et await sont des mots clés contextuels. Pour plus d’informations et d’exemples, consultez les rubriques suivantes :

Types et paramètres de retour

Une méthode asynchrone retourne généralement un Task ou un Task<TResult>. À l’intérieur d’une méthode asynchrone, un opérateur await est appliqué à une tâche retournée par un appel à une autre méthode asynchrone.

Vous spécifiez Task<TResult> comme type de retour si la méthode contient une return instruction qui spécifie un opérande de type TResult.

Vous utilisez Task comme type de retour si la méthode n’a pas d’instruction return ou a une instruction return qui ne retourne pas d’opérande.

Vous pouvez également spécifier n’importe quel autre type de retour, à condition que le type inclut une GetAwaiter méthode. ValueTask<TResult> est un exemple de ce type. Il est disponible dans le package NuGet System.Threading.Tasks.Extension .

L’exemple suivant montre comment déclarer et appeler une méthode qui retourne un Task<TResult> ou un Task:

async Task<int> GetTaskOfTResultAsync()
{
    int hours = 0;
    await Task.Delay(0);

    return hours;
}


Task<int> returnedTaskTResult = GetTaskOfTResultAsync();
int intResult = await returnedTaskTResult;
// Single line
// int intResult = await GetTaskOfTResultAsync();

async Task GetTaskAsync()
{
    await Task.Delay(0);
    // No return statement needed
}

Task returnedTask = GetTaskAsync();
await returnedTask;
// Single line
await GetTaskAsync();

Chaque tâche retournée représente le travail en cours. Une tâche encapsule des informations sur l’état du processus asynchrone et, éventuellement, le résultat final du processus ou l’exception que le processus déclenche s’il ne réussit pas.

Une méthode asynchrone peut également avoir un void type de retour. Ce type de retour est utilisé principalement pour définir des gestionnaires d’événements, où un void type de retour est requis. Les gestionnaires d’événements asynchrones servent souvent de point de départ pour les programmes asynchrones.

Une méthode asynchrone dont le type de retour est void ne peut pas être attendue, et l’appelant d’une méthode retournant void ne peut capturer aucune exception levée par la méthode.

Une méthode asynchrone ne peut pas déclarer de paramètres in, ref ou out, mais elle peut appeler des méthodes qui ont de tels paramètres. De même, une méthode asynchrone ne peut pas retourner une valeur par référence, bien qu’elle puisse appeler des méthodes avec des valeurs de retour ref.

Pour plus d’informations et d’exemples, consultez les types de retour asynchrones (C#).

Les API asynchrones de la programmation Windows Runtime ont l’un des types de retour suivants, qui sont similaires aux tâches :

Conventions d’affectation de noms

Par convention, les méthodes qui retournent des types couramment attendus (par exemple, Task, , Task<T>ValueTask, ValueTask<T>) doivent avoir des noms qui se terminent par « Async ». Les méthodes qui démarrent une opération asynchrone, mais qui ne retournent pas un type awaitable ne doivent pas avoir un nom qui se termine par « Async ». Cependant, leur nom peut commencer par « Begin », « Start » ou un autre mot suggérant que cette méthode ne retourne pas ou ne lève pas le résultat de l’opération.

Vous pouvez ignorer la convention dans laquelle un événement, une classe de base ou un contrat d’interface suggère un autre nom. Par exemple, vous ne devez pas renommer les gestionnaires d’événements courants, tels que OnButtonClick.

Articles connexes (Visual Studio)

Titre Descriptif
Comment effectuer plusieurs requêtes web en parallèle à l’aide d’async et await (C#) Montre comment démarrer plusieurs tâches en même temps.
Types de retour asynchrones (C#) Illustre les types que les méthodes asynchrones peuvent retourner et expliquent quand chaque type est approprié.
Annuler des tâches avec un jeton d’annulation en tant que mécanisme de signalisation. Montre comment ajouter les fonctionnalités suivantes à votre solution asynchrone :

- Annuler une liste de tâches (C#)
- Annuler les tâches après une période de temps (C#)
- Traiter les tâches asynchrones terminées (C#)
Utilisation d’async pour l’accès aux fichiers (C#) Répertorie et explique les avantages de l'utilisation d'async et d'await pour accéder aux fichiers.
Modèle asynchrone basé sur des tâches (TAP) Décrit un modèle asynchrone, le modèle est basé sur les types Task et Task<TResult>.
Vidéos asynchrones sur Channel 9 Fournit des liens vers une variété de vidéos sur la programmation asynchrone.

Voir aussi