Passer au contenu du pied de page
Iron Academy Logo
Nouveautés dans C# et .NET

Async C# : une exploration en profondeur de l'async zip avec Tim Corey

La compression et l'extraction de fichiers sont des tâches essentielles pour de nombreux développeurs C#, que vous travailliez sur une application de bureau, un serveur web, ou que vous ayez simplement besoin d'archiver des données. Pendant des années, les développeurs .NET pouvaient créer, mettre à jour, et extraire des fichiers zip de façon synchrone, bloquant le thread UI ou retardant d'autres opérations. Comme l'explique Tim Corey dans sa vidéo "Async Zip in .NET 10 – One Line Create or Extract And More", .NET 10 introduit des opérations asynchrones pour les fichiers zip, permettant aux développeurs d'écrire du code efficace qui effectue des opérations liées à l'I/O sans geler l'application principale.

La programmation asynchrone en C# est principalement basée sur les objets Task et Task, la classe Task servant de composant central pour modéliser le travail en cours et permettre l'exécution concurrente. Le modèle Task asynchronous programming (TAP) fournit une couche d'abstraction sur le codage asynchrone typique, le rendant plus facile à gérer les opérations asynchrones et les exceptions. La classe System.Threading.Tasks.Task et les types associés sont utilisés pour implémenter le modèle de programmation asynchrone basé sur les tâches en C#.

Dans cet article, nous suivrons l'explication de Tim et montrerons comment effectuer des opérations asynchrones en C# pour créer, extraire, et zipper sélectivement des fichiers, tout en utilisant le pattern async/await, les objets Task, et une gestion appropriée des exceptions.

Introduction à la programmation asynchrone et à Async Zip in .NET 10

Tim commence par mettre en lumière la capacité de longue date de C# à créer et extraire des fichiers zip. Cependant, avec .NET 10, les développeurs peuvent maintenant utiliser du code asynchrone pour gérer ces tâches sans bloquer le thread appelant. En utilisant les méthodes async et le mot-clé await, des opérations telles que la création ou l'extraction de grands fichiers zip peuvent s'exécuter sur des threads en arrière-plan, permettant au thread principal de l'UI de rester réactif.

Le mot-clé async n'est qu'un décorateur qui indique au compilateur C# que la méthode contient au moins une occurrence du mot-clé await. Le mot-clé await ne peut être utilisé qu'à l'intérieur d'une méthode async, et il doit être suivi par un objet Task ou Task.

Comme l'explique Tim, c'est particulièrement important dans les applications qui effectuent plusieurs tâches, telles que les appels réseau, les requêtes de base de données, ou l'I/O de fichiers, où attendre les tâches permet aux tâches liées au CPU de continuer sans bloquer le thread appelant. Le blocage du code async avec .Result ou .Wait() ramène le code async à du code synchrone, ce qui peut entraîner des deadlocks.

Les méthodes async renvoient des objets Task ou Task, et le retour des tâches permet une composition et une concurrence adéquates des tâches. Les méthodes sans await sont exécutées de manière synchrone. En renvoyant des objets Task à partir de méthodes asynchrones, vous pouvez coordonner plusieurs opérations en cours et gérer les exceptions plus efficacement.

Configuration du projet

Tim commence par ouvrir Visual Studio 2026 et créer une application console nommée ZipFilesApp. Il note que même les méthodes async Task Main statiques peuvent être utilisées pour effectuer des tâches asynchrones dans le point d'entrée du programme, évitant de bloquer le thread principal. Lors de l'adaptation du code existant, vous pouvez souvent encapsuler du code synchrone dans des Tasks ou implémenter des variantes async pour introduire un comportement asynchrone sans réécrire l'ensemble de la base de code.

Il ajoute ensuite la directive using essentielle :

using System.IO.Compression;

Cela permet d'accéder à des méthodes asynchrones telles que CreateFromDirectoryAsync et ExtractToDirectoryAsync. Lors de la création de méthodes appelantes, il est important d'utiliser des conventions de nommage cohérentes, telles que l'ajout du suffixe 'Async' aux méthodes asynchrones, pour les distinguer clairement des méthodes synchrones. Un nommage incohérent des méthodes peut conduire à de la confusion sur le fait qu'une méthode soit synchrone ou asynchrone.

Ensuite, Tim prépare trois variables string pour définir les chemins nécessaires aux opérations zip :

  1. Répertoire source – Le dossier à compresser.

  2. Fichier zip de destination – Le chemin complet pour enregistrer le fichier zip.

  3. Répertoire de destination – Le dossier où seront stockés les fichiers extraits.

Tim mentionne l'utilisation du symbole @ dans les littéraux string pour éviter d'échapper les barres obliques inverses, un problème commun dans le code synchrone lors de l'écriture des chemins Windows. Tout au long de cet article, des exemples de code et des extraits de code sont fournis pour illustrer comment mettre en œuvre la programmation asynchrone en C#.

Création d'un fichier Zip en utilisant une méthode Async

À 3:47, Tim démontre une opération asynchrone d'une ligne en utilisant une méthode async Task statique :

await ZipFile.CreateFromDirectoryAsync(
    sourceDirectory, 
    destinationZipFile, 
    CompressionLevel.SmallestSize, 
    includeBaseDirectory: false
);

Ici, Tim explique plusieurs aspects importants :

  • Le mot-clé async marque cette méthode asynchrone, permettant l'utilisation de await.

  • await met en pause l'exécution de cette méthode async jusqu'à ce que la tâche soit terminée, permettant à d'autres tâches ou au thread principal de l'UI de continuer à s'exécuter. Cela démontre comment await travaille et met en pause l'exécution pour assurer un comportement non-bloquant dans les opérations asynchrones telles que l'I/O de fichiers.

  • Le niveau de compression CompressionLevel.SmallestSize assure une compression efficace.

  • Le paramètre includeBaseDirectory contrôle si le dossier racine est inclus dans le zip.

Cet exemple montre comment le flux de travail asynchrone en C# est géré par les mots-clés async et await, rendant le code plus maintenable et simplifiant la gestion des exceptions. Le mot-clé await ne peut être utilisé qu'à l'intérieur d'une méthode async, telle qu'une méthode public async Task. Lorsque await est rencontré, le compilateur C# génère une machine à états pour gérer le flux d'exécution, mettant en pause l'exécution, démarrant la tâche asynchrone, et reprenant une fois terminée. Si la méthode async renvoie une valeur, le résultat peut être capturé dans une variable, telle que int result, pour une utilisation ultérieure. En utilisant async/await, les développeurs n'ont pas à créer manuellement des threads en arrière-plan ou des tâches de pool de threads, car le compilateur génère automatiquement la machine à états nécessaire et le code de support.

Extraction d'un fichier Zip avec l'attente Asynchrone Task

Tim passe à l'extraction d'un fichier zip avec des opérations asynchrones, en utilisant une autre méthode async :

await ZipFile.ExtractToDirectoryAsync(
    destinationZipFile, 
    destinationDirectory, 
    overwriteFiles: false
);

Il indique que le paramètre overwriteFiles gère la gestion des exceptions au cas où le dossier existerait déjà. Par défaut, l'extraction échouera si les fichiers existent déjà, mais en le définissant à true, l'opération async permet de remplacer les fichiers existants.

Lors de l'utilisation du code asynchrone, la gestion robuste des erreurs est cruciale. Tim souligne l'importance d'utiliser des blocs try-catch pour gérer les exceptions dans les méthodes async. Ne pas gérer les exceptions correctement dans le code async peut entraîner des échecs silencieux ou des plantages inattendus, car les exceptions levées dans les tâches asynchrones peuvent ne pas être immédiatement visibles. Gérez toujours les exceptions pour assurer un comportement fiable de l'application.

Tim note que ce code async est sécurisé pour plusieurs tâches, ce qui signifie qu'il ne bloquera pas d'autres requêtes ou appels réseau se produisant simultanément. En utilisant le mot-clé await, le thread appelant est libéré jusqu'à ce que la tâche se termine, créant un flux de programme réactif.

De plus, lors de la définition de méthodes async, il est important d'utiliser async void uniquement pour les gestionnaires d'événements, tels que les clics de bouton dans les applications GUI. Dans ces cas, la signature de méthode inclut généralement (object sender, EventArgs e), où object sender identifie la source de l'événement. Utiliser async void en dehors des gestionnaires d'événements peut conduire à un comportement imprévisible et à des bugs difficiles à diagnostiquer, préférez donc async Task pour la plupart des méthodes asynchrones.

Ajout sélectif de fichiers dans un Zip

Parfois, les développeurs ont besoin de plus de contrôle que simplement zipper un dossier entier. Tim démontre la réalisation d'opérations asynchrones de manière sélective en créant un FileStream :

await using FileStream zipStream = new FileStream(
    destinationZipFile, 
    FileMode.Create, 
    FileAccess.Write, 
    FileShare.None, 
    bufferSize: 4096, 
    useAsync: true
);

Lors de l'ajout de fichiers de manière asynchrone, chaque ajout de fichier représente une tâche spécifique. En démarrant toutes les tâches pour les ajouts de fichiers en même temps, puis en attendant leur achèvement avec des méthodes comme Task.WhenAll, vous pouvez améliorer considérablement les performances. Gérer les tâches en cours de cette manière est essentiel pour les scénarios à haute concurrence, comme lorsqu'un serveur doit traiter plusieurs requêtes à la fois. Cette approche est également couramment utilisée lors d'interactions avec un serveur distant, permettant aux applications de rester réactives pendant qu'elles attendent des données.

Tim explique chaque détail :

  • FileMode.Create – Assure qu'un nouveau fichier zip est créé ou écrasé.

  • FileAccess.Write – Permet d'écrire dans le fichier.

  • FileShare.None – Empêche d'autres tâches d'accéder au fichier pendant qu'il est en cours d'écriture.

  • useAsync: true – Active l'écriture de fichiers asynchrone, une opération clef async.

Il note que la programmation async dans ce contexte évite de bloquer le thread principal pendant que la tâche se termine, ce qui est particulièrement utile sur les serveurs web traitant plusieurs tâches. Les serveurs à haute concurrence exploitent la programmation asynchrone pour gérer efficacement plusieurs requêtes clientes simultanées sans créer un nouveau thread pour chaque connexion. Cependant, écrire du code async séquentiellement peut le rendre plus difficile à lire et à débugger, car les tâches async peuvent se terminer dans le désordre.

Création de l'archive Zip et ajout de fichiers

Ensuite, Tim crée une ZipArchive de manière asynchrone :

using ZipArchive archive = await ZipArchive.CreateAsync(
    zipStream, 
    ZipArchiveMode.Create, 
    leaveOpen: false, 
    entryNameEncoding: null
);
  • L'expression await garantit que l'exécution est mise en pause jusqu'à la fin de la tâche, ce qui permet de continuer en toute sécurité à ajouter des fichiers. Async et await simplifient la gestion des opérations asynchrones, rendant le code plus facile à lire et à maintenir par rapport aux approches traditionnelles basées sur les callbacks.

  • Il est important de noter que le même code peut se comporter différemment selon le contexte de synchronisation. Par exemple, dans les applications WPF ou WinForms, la présence d'un contexte de synchronisation peut faire en sorte que la continuation après un appel await s'exécute sur le thread principal de l'UI, alors que dans les applications console, il n'y a pas de tel contexte. Cela peut affecter la manière dont votre code async s'exécute.

  • Lors de l'écriture de code de bibliothèque, envisagez d'utiliser ConfigureAwait(false) après un appel await pour éviter les commutations de contexte inutiles et améliorer les performances.

  • En utilisant Path.GetRelativePath, Tim explique comment maintenir la structure des dossiers à l'intérieur du zip, ce qui est essentiel lors de la réalisation de flux de travail asynchrones avec plusieurs fichiers :
foreach (string filePath in Directory.GetFiles(sourceDirectory, '*', SearchOption.AllDirectories))
{
    string relativePath = Path.GetRelativePath(sourceDirectory, filePath);
    await archive.CreateEntryFromFileAsync(filePath, relativePath).ConfigureAwait(false);
}

Cela démontre du code async en pratique, où chaque ajout de fichier est un appel asynchrone, et le mot-clé await assure un enchaînement correct tout en effectuant un I/O non-bloquant. Le code asynchrone permet au thread actuel de rester non-bloqué, améliorant la réactivité et permettant au thread d'effectuer d'autres travaux tout en attendant que les opérations d'I/O se terminent.

Gestion de l'accès aux fichiers et gestion des exceptions

Tim met en avant un scénario commun de gestion des exceptions : tenter d'extraire un zip avant que la tâche FileStream originale ne soit terminée. L'utilisation de méthodes async void ou async Task au niveau du fichier sans déclarations using appropriées peut entraîner :

Le processus ne peut accéder au fichier car il est utilisé par un autre processus.

La solution consiste à envelopper l'opération asynchrone dans un bloc using traditionnel pour libérer les ressources avant l'extraction :

await using (FileStream zipStream = new FileStream(...))
{
    using ZipArchive archive = await ZipArchive.CreateAsync(zipStream, ...);
    // Ajouter des fichiers de manière asynchrone
}
// Il est maintenant sûr d'extraire

Cela garantit que l'objet de tâche se termine et libère le contexte de synchronisation, évitant ainsi les erreurs de verrouillage de fichier.

Conclusion : Zip asynchrone efficace dans .NET 10

Tim conclut que les opérations zip asynchrones dans .NET 10 permettent aux développeurs d'effectuer des opérations asynchrones efficacement, sans bloquer le thread principal, et de s'intégrer parfaitement avec d'autres tâches comme les appels réseau ou les requêtes de base de données.

En utilisant des méthodes async Task statiques, des modificateurs async, et des expressions await, les développeurs peuvent :

  • Compresser ou extraire des fichiers zip sans geler le thread de l'interface utilisateur.

  • Gérer plusieurs threads en toute sécurité sans gestion manuelle de pool de threads.

  • Effectuer une compression sélective tout en préservant les structures de dossiers.

  • Assurer une gestion correcte des exceptions et le nettoyage des ressources.

Les exemples de Tim démontrent que async/await dans les opérations de fichiers n'est pas seulement pratique, c'est une partie essentielle de la programmation asynchrone moderne en C#.

En suivant sa vidéo, les développeurs peuvent écrire du code efficace en utilisant des méthodes async, des appels await, et des workflows asynchrones pour gérer les tâches de compression de fichiers simples et complexes en toute transparence.

Hero Worlddot related to Async C# : une exploration en profondeur de l'async zip avec Tim Corey
Hero Affiliate related to Async C# : une exploration en profondeur de l'async zip avec Tim Corey

Gagnez plus en partageant ce que vous aimez

Vous créez du contenu pour les développeurs travaillant avec .NET, C#, Java, Python ou Node.js ? Transformez votre expertise en revenu supplémentaire !

Équipe de soutien Iron

Nous sommes en ligne 24 heures sur 24, 5 jours sur 7.
Chat
Email
Appelez-moi