Concurrence et multithreading en C# : niveau avancé

Formateur Python en ligne pour débutants

Apprenez Python facilement sans surcharge théorique. Résolvez des tâches pratiques avec vérification automatique et écrivez du code directement dans le navigateur.

Start Course

Concurrence et multithreading en C# : niveau avancé



Dans le développement moderne en C#, la concurrence et le multithreading ne sont pas simplement des extensions de capacités, mais des outils fondamentaux pour créer des applications rapides, réactives et évolutives. Au niveau avancé, nous dépassons le simple lancement de Task.Run et rencontrons les subtilités des planificateurs, la synchronisation des contextes et les patterns pour travailler avec des données partagées. Dans cet article, nous analyserons les concepts avancés clés de la concurrence en C# (.NET) qui vous aideront à écrire un code efficace et sûr.



1. Task et ValueTask : utilisation approfondie



La classe Task est la base de la programmation asynchrone en C#. Cependant, au niveau avancé, il est important de comprendre la différence entre Task et ValueTask, ainsi que de gérer le cycle de vie des tâches.



Quand utiliser ValueTask ?

ValueTask est une structure qui permet d'éviter l'allocation mémoire pour un objet Task lorsque le résultat est déjà disponible de manière synchrone. C'est crucial pour les scénarios à haute performance, par exemple dans les méthodes de mise en cache.



public class CacheService{    private readonly Dictionary<string, string> _cache = new();

public async ValueTask<string> GetDataAsync(string key) { if (_cache.TryGetValue(key, out string value)) { // Le résultat est déjà présent, on ne crée pas de Task return value; }

// Récupération asynchrone des données string data = await FetchFromDatabaseAsync(key); _cache[key] = data; return data; }

private async Task<string> FetchFromDatabaseAsync(string key) { await Task.Delay(100); // Simulation d'un délai return $"Data for {key}"; }}


Gestion avancée des erreurs et annulation

Utilisez CancellationToken pour annuler correctement les opérations longues. Combinez plusieurs jetons via CancellationTokenSource.CreateLinkedTokenSource.



public async Task ProcessWithTimeoutAsync(CancellationToken externalToken){    using var timeoutCts = new CancellationTokenSource(TimeSpan.FromSeconds(5));    using var linkedCts = CancellationTokenSource.CreateLinkedTokenSource(externalToken, timeoutCts.Token);

try { await LongRunningOperationAsync(linkedCts.Token); } catch (OperationCanceledException) { Console.WriteLine("Opération annulée ou délai dépassé."); }}


2. SynchronizationContext et planificateurs de tâches



La compréhension de SynchronizationContext est nécessaire pour travailler avec les threads d'interface utilisateur (WPF, WinForms) et ASP.NET Core. Au niveau avancé, vous pouvez créer vos propres contextes de synchronisation.



Configuration du contexte pour le pool de threads

Par défaut, await capture le SynchronizationContext actuel. Pour éviter cela (et prévenir les deadlocks), utilisez ConfigureAwait(false).



public async Task ProcessDataAsync(){    // Ce code s'exécute dans le contexte d'origine (par exemple, UI)    await Task.Run(() => HeavyComputation()).ConfigureAwait(false);

// Après ConfigureAwait(false) — dans le contexte du pool de threads // Pas d'accès aux éléments d'interface utilisateur ! Console.WriteLine("Exécuté dans un thread du pool");}


Création d'un planificateur personnalisé

Vous pouvez implémenter votre propre TaskScheduler, par exemple pour limiter le nombre de tâches exécutées simultanément (équivalent de SemaphoreSlim au niveau du planificateur).



public class LimitedConcurrencyTaskScheduler : TaskScheduler{    private readonly SemaphoreSlim _semaphore;    private int _runningTasks;

public LimitedConcurrencyTaskScheduler(int maxConcurrency) { _semaphore = new SemaphoreSlim(maxConcurrency);

Blogs

Book Recommendations