Bases de la programmation asynchrone en C# (async/await)
Apprenez async et await en C# pour créer des applications réactives avec des tâches asynchrones et des exemples pratiques.
Dans les applications modernes, la programmation asynchrone est essentielle
pour garder l’interface utilisateur réactive et exécuter des opérations longues
sans bloquer ou geler l’application.
En C#, les mots-clés async et await sont utilisés à cet effet.
Cette structure permet d’exécuter des tâches de longue durée en arrière-plan
tout en maintenant la réactivité de l’application.
Qu’est-ce que la programmation asynchrone ?
La programmation asynchrone permet à d’autres opérations de continuer pendant qu’une tâche est en attente d’exécution. Elle est particulièrement utile pour les opérations I/O-bound telles que la lecture de fichiers, les requêtes réseau, les accès à une base de données ou les appels d’API.
// Synchrone : les opérations s’exécutent l’une après l’autre
LireFichier();
EnvoyerDonnees();
Console.WriteLine("Terminé !");
// Asynchrone : les opérations se poursuivent sans attendre
await LireFichierAsync();
await EnvoyerDonneesAsync();
Console.WriteLine("Terminé !");
Les mots-clés async et await
Le mot-clé async rend une méthode asynchrone.
À l’intérieur de cette méthode, le mot-clé await est utilisé pour attendre
la fin d’une autre opération asynchrone.
Cela permet au thread de continuer son exécution sans être bloqué.
async Task EffectuerOperationAsync()
{
Console.WriteLine("Opération démarrée...");
await Task.Delay(2000); // Attend 2 secondes sans bloquer le thread
Console.WriteLine("Opération terminée !");
}
Dans l’exemple ci-dessus, même si Task.Delay introduit un délai,
l’application ne se fige pas car la tâche attend en arrière-plan sans bloquer le processeur.
Types Task et Task<T>
Les méthodes asynchrones retournent généralement un Task ou un Task<T>.
Task indique simplement que l’opération est terminée,
tandis que Task<T> retourne un résultat.
// Méthode asynchrone sans retour de valeur
async Task EnregistrerFichierAsync(string nom)
{
await File.WriteAllTextAsync(nom, "Contenu du fichier...");
}
// Méthode asynchrone avec valeur de retour
async Task<string> ObtenirDonneesAsync()
{
await Task.Delay(1000);
return "Données reçues du serveur";
}
// Utilisation :
string resultat = await ObtenirDonneesAsync();
Console.WriteLine(resultat);
Méthode Main asynchrone
Depuis C# 7.1, la méthode Main peut également être asynchrone.
Cela permet d’utiliser await directement dans une application console.
class Program
{
static async Task Main()
{
Console.WriteLine("Récupération des données...");
string data = await ObtenirDonneesAsync();
Console.WriteLine($"Résultat : {data}");
}
static async Task<string> ObtenirDonneesAsync()
{
await Task.Delay(1500);
return "Terminé !";
}
}
Points importants lors de l’utilisation de await
- await ne peut être utilisé qu’à l’intérieur d’une méthode marquée avec
async. - Plusieurs tâches asynchrones peuvent être exécutées simultanément (
Task.WhenAll()ouTask.WhenAny()). async voiddoit uniquement être utilisé pour les gestionnaires d’événements.
// Exécuter plusieurs tâches en parallèle
var tache1 = LireFichierAsync();
var tache2 = EnvoyerDonneesAsync();
await Task.WhenAll(tache1, tache2);
Console.WriteLine("Les deux tâches sont terminées.");
Gestion des erreurs dans le code asynchrone
Les erreurs dans les méthodes asynchrones peuvent être gérées à l’aide de blocs try-catch.
Si une tâche en attente via await lève une exception,
le bloc catch sera exécuté.
try
{
await LireFichierAsync();
}
catch (IOException ex)
{
Console.WriteLine($"Erreur lors de la lecture du fichier : {ex.Message}");
}
Exemple : Récupération de données depuis une API
L’exemple suivant montre une requête HTTP asynchrone en utilisant HttpClient.
Le mot-clé await empêche l’application de se figer
pendant l’attente de la réponse du réseau.
using System;
using System.Net.Http;
using System.Threading.Tasks;
class Program
{
static async Task Main()
{
using HttpClient client = new HttpClient();
string url = "https://jsonplaceholder.typicode.com/posts/1";
Console.WriteLine("Requête en cours...");
string json = await client.GetStringAsync(url);
Console.WriteLine("JSON reçu :");
Console.WriteLine(json);
}
}
Exemple WPF : Opération asynchrone et mise à jour de l’interface
Dans l’exemple suivant, lorsqu’un bouton est cliqué dans une application WPF,
une opération longue (simulée avec Task.Delay) est exécutée de manière asynchrone.
Grâce au mot-clé await, l’interface utilisateur reste réactive pendant l’exécution,
et le contenu du TextBox peut être mis à jour sans geler l’application.
// MainWindow.xaml
<Window x:Class="AsyncExample.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Exemple Async / Await" Height="250" Width="400">
<Grid Margin="20">
<StackPanel>
<TextBlock Text="Exemple d’opération asynchrone" FontWeight="Bold" FontSize="16" Margin="0,0,0,10"/>
<TextBox x:Name="txtStatut" Height="30" Margin="0,0,0,10"
VerticalContentAlignment="Center" FontSize="14"/>
<Button x:Name="btnDemarrer" Height="35" Content="Démarrer l’opération"
Click="btnDemarrer_Click" FontSize="14"/>
</StackPanel>
</Grid>
</Window>
// MainWindow.xaml.cs
using System;
using System.Threading.Tasks;
using System.Windows;
namespace AsyncExample
{
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private async void btnDemarrer_Click(object sender, RoutedEventArgs e)
{
// async void est autorisé ici car c’est un gestionnaire d’événement
txtStatut.Text = "Opération démarrée...";
btnDemarrer.IsEnabled = false; // désactiver le bouton
// Simulation d’une opération longue (exemple : téléchargement de fichier)
await Task.Delay(3000);
txtStatut.Text = "Opération terminée !";
btnDemarrer.IsEnabled = true;
}
}
}
// Déroulement :
1. L’utilisateur clique sur le bouton « Démarrer l’opération ».
2. Le TextBox affiche « Opération démarrée... ».
3. Attente de 3 secondes (l’interface reste réactive).
4. Une fois terminé, le TextBox affiche « Opération terminée ! ».
Remarque : Si ce code était exécuté de manière synchrone (par exemple avec Thread.Sleep()),
l’interface serait figée, et le texte « Opération démarrée... » ne s’afficherait qu’à la fin.
L’utilisation de await Task.Delay() évite complètement ce problème.
Exemple WPF : Mise à jour du TextBox depuis une tâche (avec Dispatcher)
Dans cet exemple, une opération longue est exécutée dans une tâche Task séparée.
Les mises à jour de txtStatut.Text sont effectuées sur le thread de l’interface
grâce à Dispatcher.Invoke.
Cela évite les erreurs d’accès multi-threads.
// MainWindow.xaml
<Window x:Class="AsyncExample.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Exemple Dispatcher" Height="250" Width="400">
<Grid Margin="20">
<StackPanel>
<TextBlock Text="Mise à jour de l’interface dans une tâche" FontWeight="Bold" FontSize="16" Margin="0,0,0,10"/>
<TextBox x:Name="txtStatut" Height="30" Margin="0,0,0,10"
VerticalContentAlignment="Center" FontSize="14"/>
<Button x:Name="btnDemarrer" Height="35" Content="Démarrer une opération longue"
Click="btnDemarrer_Click" FontSize="14"/>
</StackPanel>
</Grid>
</Window>
// MainWindow.xaml.cs
using System;
using System.Threading.Tasks;
using System.Windows;
namespace AsyncExample
{
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private async void btnDemarrer_Click(object sender, RoutedEventArgs e)
{
btnDemarrer.IsEnabled = false;
txtStatut.Text = "Processus démarré...";
// Exécution de l’opération longue dans une tâche séparée
await Task.Run(() =>
{
for (int i = 1; i <= 5; i++)
{
// Simulation d’un travail
Task.Delay(1000).Wait();
// Mise à jour de l’interface via Dispatcher
Dispatcher.Invoke(() =>
{
txtStatut.Text = $"Étape {i} terminée...";
});
}
});
txtStatut.Text = "Toutes les étapes sont terminées !";
btnDemarrer.IsEnabled = true;
}
}
}
// Déroulement :
1. L’utilisateur clique sur le bouton « Démarrer une opération longue ».
2. Un processus en 5 étapes démarre dans Task.Run.
3. Chaque seconde, le TextBox est mis à jour avec Dispatcher.Invoke.
4. L’interface reste réactive pendant tout le processus.
Remarque :
Si Dispatcher.Invoke() était remplacé par un simple txtStatut.Text = ...,
une erreur se produirait en raison d’un accès depuis un thread non-UI.
Alternativement, Dispatcher.BeginInvoke() peut être utilisé,
ce qui n’empêche pas le thread de l’interface de continuer.
TL;DR (Résumé)
asyncrend une méthode asynchrone,awaitattend la fin de l’opération.TasketTask<T>représentent les opérations asynchrones et leurs résultats.awaitne bloque pas le processeur ; l’exécution continue après la fin de la tâche.- Avec
Task.WhenAll(), plusieurs tâches peuvent être exécutées simultanément. - Les erreurs sont gérées avec
try-catch;async voidne doit être utilisé que dans les gestionnaires d’événements.
Articles connexes
Bibliothèque parallèle TPL et programmation parallèle en C#
Apprenez la Task Parallel Library et la programmation parallèle en C# avec Task, Parallel et des exemples pratiques.
Flux asynchrones en C# (IAsyncEnumerable)
Apprenez les flux asynchrones en C# avec IAsyncEnumerable pour traiter les données progressivement avec des exemples.
Gestion des processus et des threads en C#
Apprenez la gestion des processus et des threads en C# pour contrôler l’exécution et les ressources système.