Pourquoi votre application C# a besoin de files d'attente et de tâches de fond ?
\n\nImaginez : vous lancez une application web en C#, un utilisateur clique sur le bouton « Envoyer le rapport », et… le serveur se bloque pendant 30 secondes pendant que le PDF est généré. Familier ? En 2024, les utilisateurs ne pardonnent pas les ralentissements. La solution : les files d'attente et les tâches de fond. Elles transforment l'enfer synchrone en paradis asynchrone.
\n\nDans cet article, nous allons voir comment organiser correctement le traitement en arrière-plan en C# : des Channel<T> intégrés au RabbitMQ industriel. Vous apprendrez à déplacer le travail lourd hors du thread principal sans casser l'architecture. C'est parti !
Qu'est-ce qu'une file d'attente de tâches et pourquoi en C# ?
\n\nUne file d'attente (queue) est une structure de données FIFO (First In, First Out). Dans le contexte de C#, c'est un mécanisme qui permet de différer l'exécution d'une tâche pour ne pas bloquer le thread principal. Une tâche de fond est un code qui s'exécute « en coulisses » pendant que l'utilisateur continue de cliquer sur le site.
\n\nScénarios typiques :
\n- \n
- Envoi de notifications par email (100 emails = 100 requêtes SMTP) \n
- Génération de rapports et de fichiers Excel \n
- Traitement des images téléchargées (redimensionnement, filigrane) \n
- Intégration avec des API externes (réponses lentes) \n
- Journalisation en base de données (pour ne pas ralentir l'interface utilisateur) \n
Sans files d'attente, une application C# « bloquera » sur chaque requête lourde. Avec les files d'attente, vous placez simplement la tâche dans un tampon et répondez à l'utilisateur : « Terminé, nous traitons ».
\n\nOption 1 : Tâches de fond avec BackgroundService (.NET intégré)
\n\nLa méthode la plus simple consiste à utiliser BackgroundService de Microsoft.Extensions.Hosting. C'est une classe de base pour créer des services de fond dans ASP.NET Core. Elle est parfaite pour les scénarios simples où vous n'avez pas besoin d'un routage de messages complexe.
Exemple : gestionnaire de fond avec Channel<T>
\n\nÉcrivons un simple Producer-Consumer. Le Producer (contrôleur) place une tâche dans le canal, le Consumer (BackgroundService) la récupère et l'exécute.
\n\n// Étape 1. Créer un canal (file d'attente thread-safe)\npublic class TaskChannel\n{\n private readonly Channel<string> _channel = \n Channel.CreateBounded<string>(new BoundedChannelOptions(100)\n {\n FullMode = BoundedChannelFullMode.Wait\n });\n\n public ChannelWriter<string> Writer => _channel.Writer;\n public ChannelReader<string> Reader => _channel.Reader;\n}\n\n// Étape 2. BackgroundService qui lit depuis le canal\npublic class EmailBackgroundService : BackgroundService\n{\n private readonly TaskChannel _taskChannel;\n\n public EmailBackgroundService(TaskChannel taskChannel)\n {\n _taskChannel = taskChannel;\n }\n\n protected override async Task ExecuteAsync(CancellationToken stoppingToken)\n {\n await foreach (var emailData in _taskChannel.Reader.ReadAllAsync(stoppingToken))\n {\n // Simulation d'envoi d'email (2 secondes)\n await Task.Delay(2000, stoppingToken);\n Console.WriteLine($"Email envoyé : {emailData}");\n }\n }\n}\n\n// Étape 3. Contrôleur qui place les tâches\n[ApiController]\n[Route("api/email")]\npublic class EmailController : ControllerBase\n{\n private readonly TaskChannel _taskChannel;\n\n public EmailController(TaskChannel taskChannel)\n {\n _taskChannel = taskChannel;\n }\n\n [HttpPost("send")]\n public IActionResult SendEmail([FromBody] string emailData)\n {\n _taskChannel.Writer.TryWrite(emailData);\n return Accepted(new { message = "Email mis en file d'attente" });\n }\n}\n\n// Étape 4. Enregistrement dans Program.cs\nbuilder.Services.AddSingleton<TaskChannel>();\nbuilder.Services.AddHostedService<EmailBackgroundService>();\n\nQue se passe-t-il ici ? L'utilisateur appelle POST /api/email/send — les données entrent dans le ca