CodeGym /Cours /C# SELF /Avantages du travail asynchrone avec les fichiers ( async...

Avantages du travail asynchrone avec les fichiers ( async/ await)

C# SELF
Niveau 42, Leçon 0
Disponible

1. Introduction

Imagine que votre application est un café. Et dans ce café il n'y a qu'un seul serveur (c'est notre thread principal d'exécution). Quand un client (l'utilisateur) commande un café (une action), le serveur va en cuisine, prépare le café et ne revient à la table que lorsqu'il a fini, pour prendre la commande suivante.

Et maintenant imaginez que quelqu'un commande... un bortsch. Pas n'importe quel bortsch, mais une énorme marmite qui doit cuire pendant deux heures ! Que va faire notre serveur ? Il va rester devant la cuisinière pendant ces deux heures, à ne rien faire, juste attendre que le bortsch soit prêt. Tous les autres clients vont rester assis, faire signe, se plaindre, et il ne les verra tout simplement pas. Le café est "bloqué", parce que le serveur est bloqué.

En programmation, on appelle ça une opération bloquante. Quand vous appelez une méthode classique pour lire ou écrire un fichier (par exemple, FileStream.Read() ou StreamReader.ReadLine()), votre thread courant d'exécution est bloqué. Il arrête d'exécuter tout le reste du code jusqu'à ce que l'opération d'I/O soit terminée.

Regardons un exemple simple :


// Créons un fichier "gros" pour la démonstration
string largeFilePath = "LargeOrder.txt";
using (StreamWriter sw = new StreamWriter(largeFilePath))
{
    for (int i = 0; i < 1000000; i++) // 1 million de lignes
        sw.WriteLine($"Ligne {i} : Une information très importante...");
} // Le fichier est fermé ici pour pouvoir le lire

// !!! ATTENTION : C'est une opération bloquante !!!
string content = File.ReadAllText(largeFilePath);

Exécutez ce code. Vous verrez que pendant l'écriture et la lecture des fichiers, le programme "gèle". La console n'accepte pas d'entrée, aucun nouveau message n'est affiché, jusqu'à ce que le fichier soit complètement lu. Ce n'est qu'après que les lignes suivantes s'exécuteront.

Ça peut être moins visible dans de petits programmes en console, mais imaginez :

  • Application avec interface graphique (UI) : Vous appuyez sur le bouton "Charger le fichier", et la fenêtre du programme "se fige". Impossible de déplacer la fenêtre, cliquer d'autres boutons, les menus ne répondent pas. Très mauvaise expérience utilisateur.
  • Serveur web : Le serveur traite les requêtes des utilisateurs. Si une requête nécessite la lecture d'un fichier très volumineux, alors toutes les autres requêtes attendront en file jusqu'à ce que ce thread soit libéré. Ça conduit à des latences énormes et une faible scalabilité.

C'est pour ça qu'on a besoin d'asynchronisme !

2. Avantages du travail asynchrone avec les fichiers

L'asynchronisme ne rend pas le disque plus rapide. Le disque fonctionnera toujours à la même vitesse. L'asynchronisme consiste à ne pas attendre qu'une opération lente se termine, mais à libérer le thread pour d'autres tâches.

Retour à notre café. Maintenant notre serveur est malin et multitâche. Quand un client commande "VRAIMENT GROS BORTSCH" (lecture d'un grand fichier), le serveur ne reste pas devant la cuisinière. Il met le bortsch à cuire, puis retourne en salle et commence à prendre d'autres commandes, nettoyer les tables, servir d'autres clients. Dès que le bortsch est prêt, la cuisine l'appelle, il revient, prend le bortsch et le sert au client.

Différence clé : le serveur n'est pas bloqué à attendre le bortsch. Il utilise productivement ce temps pour d'autres tâches.

Réactivité de l'application (User Interface Responsiveness)

C'est sans doute l'avantage le plus visible et important pour la plupart des applications desktop et mobiles. Si votre programme fait quelque chose de long (lit un fichier, télécharge des données depuis Internet, traite de gros volumes), l'approche asynchrone permet de :

  • Conserver l'interactivité de l'UI : L'utilisateur peut continuer à cliquer sur des boutons, déplacer des fenêtres, consulter d'autres informations pendant qu'une opération lourde se déroule en arrière-plan.
  • Afficher des indicateurs de progression : Vous pouvez afficher une jolie animation de chargement ou une barre de progression, pour montrer que l'application ne s'est pas plantée, mais travaille.

Imaginez qu'au lieu d'une console "figée" pendant que le serveur lit un fichier, vous verriez qu'il continue de prendre d'autres "commandes" (par exemple afficher un autre message ou réagir à l'entrée utilisateur), pendant que la lecture du fichier se passe quelque part "en arrière-plan". Bien mieux !

Utilisation efficace des ressources et scalabilité

C'est critique pour les applications serveur (par ex. web services, API, backends) qui doivent servir beaucoup d'utilisateurs en même temps.

  • On ne gaspille pas les threads : En modèle synchrone, chaque requête longue bloque un thread sur le serveur. Si vous avez 1000 requêtes comme ça, il vous faudra 1000 threads. Chaque thread consomme mémoire et des ressources CPU. L'OS passe du temps à switcher entre ces threads. L'asynchronisme permet au même thread, pendant qu'il attend la fin d'une opération I/O, de commencer à traiter une autre requête. Dès que l'I/O est fini, il revient à la première requête.
  • On libère le CPU : Pendant que les données sont lues depuis le disque ou transférées via le réseau, le processeur est inactif. L'asynchronisme lui permet de faire d'autres calculs utiles pendant ce temps.
  • Moins de mémoire : Moins de threads actifs signifie moins de consommation mémoire pour le serveur.

3. Simplification du code (en C# avec async/await)

Avant, écrire du code asynchrone était compliqué, verbeux et source d'erreurs. Il fallait gérer les threads manuellement, les callbacks et la synchronisation. C'était comme essayer de construire une station spatiale en LEGO dans le noir.

Mais en C#, les mots-clés async et await sont arrivés. C'est comme une baguette magique qui permet d'écrire du code asynchrone presque aussi simplement et lisiblement que du code synchrone. Vous dites simplement au compilateur : "Ici ça peut durer, attends, mais ne bloque pas tout le monde".


// CECI EST UN EXEMPLE de ce à quoi peut ressembler du code asynchrone (sans explications approfondies pour l'instant)
// Dans les prochaines leçons on décortiquera tout !

public static async Task Main(string[] args) // Voilà à quoi ressemble un Main asynchrone
{
    Console.WriteLine("Le programme commence à lire UN TRÈS GROS FICHIER de façon asynchrone...");

    Stopwatch stopwatch = Stopwatch.StartNew();
    
    // !!! ATTENTION : Opération asynchrone !!!
    await File.ReadAllTextAsync(largeFilePath); // Ça ne bloque pas le thread
    
    stopwatch.Stop();

    Console.WriteLine($"Fichier lu ! Temps écoulé : {stopwatch.ElapsedMilliseconds} ms.");
    // Ici il pourrait y avoir d'autres travaux pendant la lecture du fichier !
}

Remarquez : l'asynchronisme n'accélère pas l'opération I/O sur le disque lui-même. Si la lecture d'un fichier de 1 Go prend 15 secondes, elle prendra toujours 15 secondes. La différence est dans ce que votre CPU et vos threads font pendant ces secondes. En synchrone ils restent inactifs, en asynchrone ils travaillent sur d'autres tâches.

Faisons un petit tableau récapitulatif :

Caractéristique Opération synchrone (Read/Write classique) Opération asynchrone (ReadAsync/WriteAsync)
Thread d'exécution Bloqué jusqu'à la fin de l'opération Non bloqué, libéré pour d'autres tâches
Réactivité UI L'application "se fige" L'application reste interactive
Utilisation CPU Inactif pendant l'attente I/O Peut effectuer d'autres tâches pendant l'attente I/O
Scalabilité Faible (nécessite beaucoup de threads pour des opérations simultanées) Élevée (traite beaucoup de requêtes avec peu de threads)
Complexité d'écriture Simple Était complexe, mais async/await ont grandement simplifié
Vitesse de l'I/O elle-même Ne s'accélère pas Ne s'accélère pas (la vitesse dépend du disque)
Quand l'utiliser ? Pour les opérations rapides et courtes Pour toute opération potentiellement longue (I/O, réseau, DB)

En substance, l'asynchronisme est un moyen de rendre votre application plus réactive et plus scalable, surtout quand il s'agit d'interagir avec des ressources externes et lentes comme le système de fichiers ou le réseau. Ce n'est pas un remplacement de la bufferisation, mais un complément. La bufferisation accélère le transfert de données lui-même, l'asynchronisme garantit que votre programme ne restera pas figé pendant ce transfert.

4. Dans les coulisses

Exemples du monde réel : éditeurs vidéo, jeux, sites

Presque tous les programmes modernes qui travaillent avec de gros fichiers utilisent des approches asynchrones. Les lecteurs médias populaires ne bloquent pas l'interface en chargeant un film. Les serveurs ne "plantent" pas quand un client télécharge un gros fichier. Même un simple outil de backup ou un client cloud fait tout "en arrière-plan" pour que l'utilisateur puisse continuer à travailler.

Comment ça marche (en termes simples)

Les méthodes de fichiers asynchrones en .NET (par exemple, ReadAsync, WriteAsync) utilisent en réalité les capacités du système d'exploitation qui permettent de ne pas bloquer le thread du programme pendant des opérations longues. C'est possible grâce à des appels système qui disent à l'OS : « Lis ce fichier pour moi, et quand tu as fini, préviens-moi ».

Élément visuel : comment marche la lecture asynchrone (schéma)

sequenceDiagram
    participant UserCode as Votre code
    participant OS as Système d'exploitation
    participant Disk as Disque

    UserCode->>OS: Requête de lecture de fichier asynchrone
    OS->>Disk: Lit les données
    UserCode->>UserCode: Continue d'exécuter d'autres tâches
    OS->>OS: Attend la fin de la lecture
    Disk-->>OS: Les données sont prêtes
    OS-->>UserCode: Notification de fin de lecture
    UserCode->>UserCode: Traite les données

5. Où l'asynchronisme apporte le plus d'avantage

  • Applications avec interface graphique (l'UI ne se bloque pas).
  • Serveurs qui traitent de nombreuses requêtes simultanées sur des fichiers.
  • Scripts qui traitent de gros volumes de données en mode automatique (par ex. sauvegarde).
  • Outils qui travaillent avec des disques lents ou des disques réseau.

Plus les données sont volumineuses et plus le support est lent — plus l'avantage de l'asynchronisme est notable. Même si vous ne développez pas de grandes applications, il est utile de s'habituer aux méthodes Async : leur prise en charge est un standard du C# moderne.

Maintenant vous savez pourquoi le travail asynchrone avec les fichiers n'est pas juste une mode, mais une technique importante pour créer des applications rapides et réactives sur .NET 9. Dans les prochaines leçons, nous creuserons la syntaxe, la pratique et les scénarios typiques d'utilisation des méthodes ReadAsync, WriteAsync et leurs collègues !

6. Asynchronisme

Comment l'asynchronisme fonctionne exactement, nous l'étudierons dans les niveaux 55-62. Pour l'instant je veux juste vous le présenter. Si vous n'avez rien compris à ce niveau — ce n'est pas grave. Passez ces leçons et exercices et revenez-y après avoir étudié l'asynchronisme.

Parallélisme vs asynchronisme

Si vous lancez plusieurs applications Windows sur votre ordinateur et qu'elles font quelque chose en même temps, les programmeurs diront que les tâches s'exécutent en parallèle.

Si vous avez minimisé un jeu sur votre téléphone et êtes passé à autre chose, et que l'application minimisée est en pause, c'est plus proche du fonctionnement asynchrone. L'asynchronisme, ce n'est pas tant l'exécution simultanée que le fait d'attendre simultanément.

Exemple

Supposons que vous décidiez de faire les tâches ménagères. Vous lancez le lave-vaisselle, et pendant qu'il tourne vous chargez la machine à laver. Pendant que la machine lave, vous mettez un gâteau au four. Vous travaillez seul, mais vous faites plusieurs choses à la fois, parce que vous basculez sur autre chose au lieu d'attendre la fin d'une opération spécifique.

Quand la machine a fini de laver, elle vous prévient et vous revenez à la machine. Du point de vue de la machine, vous avez juste attendu qu'elle finisse. Pourtant, vous avez été actif tout le temps.

Vous ne pouvez pas simultanément charger le lave-vaisselle, mettre le gâteau et sortir le linge de la machine. Mais si l'une de ces choses est occupée, vous n'êtes pas obligé d'attendre bêtement ; vous pouvez utiliser ce temps utilement.

Un détail important

Si vous n'avez qu'une seule tâche, vous ne verrez pas de différence entre "attendre que la tâche finisse" et "avoir la possibilité de faire autre chose pendant l'attente". Mais si vous avez beaucoup de tâches, la différence devient évidente.

7. Erreurs typiques et pièges

Le malentendu le plus courant : appeler simplement une méthode asynchrone n'accélère pas automatiquement tout. En pratique, si elle est mal utilisée (par exemple, oublier le await), le code devient « imprévisible » : le résultat n'est pas prêt et le programme l'utilise quand même. Dans les applications graphiques, le traitement des événements doit presque toujours être asynchrone, sinon l'interface va "se figer".

Autre point : l'asynchronisme ne consiste pas à accélérer la lecture/écriture elle-même, mais à permettre à votre programme d'être efficace et réactif pendant les opérations lentes.

Et après ?

Maintenant que nous avons compris le pourquoi de l'asynchronisme, il est temps d'apprendre le comment. Dans la prochaine leçon nous approfondirons la syntaxe de la lecture et de l'écriture asynchrones, nous rencontrerons les versions asynchrones des méthodes (par ex. ReadAsync et WriteAsync) et nous écrirons notre premier vrai code asynchrone ! Ça va être intéressant, ne changez pas de chaîne !

2
Mission
C# SELF, niveau 42, leçon 0
Bloqué
Lecture de fichier de façon asynchrone
Lecture de fichier de façon asynchrone
Commentaires
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION