Niveau :


7 minutes de lecture

L’asynchronisme en C# a connu une révolution silencieuse mais décisive. En 2010, avec l’arrivée de .NET Framework 4, la classe Task a ouvert la voie à une programmation parallèle plus intuitive, posant les bases du modèle async/await qui deviendra incontournable dès C# 5.0. Sept ans plus tard, en 2017, .NET Core 2.0 introduit ValueTask, une alternative plus légère et performante, pensée pour les scénarios où chaque allocation compte. Deux jalons, deux philosophies, une même ambition : rendre le code asynchrone plus fluide, plus rapide, et plus adapté aux besoins modernes.
Qu’est ce que Task ?
Task est une représentation d’une opération asynchrone. Elle encapsule le travail en cours ou futur, permettant à votre programme de continuer à s’exécuter sans attendre la fin de cette opération.

Les avantages sont multiples comme la facilité d’utilisation et la compatibilité avec tout le système .Net. De plus, il est idéal pour les opérations longues ou complexes.
Les inconvénients sont l’allocation mémoire, même et surtout si le résultat est déjà connu, ainsi que la performance sur des scénarios à faible latence.
Qu’est ce que ValueTask ?
ValueTask<T> est une structure (struct) introduite pour éviter les allocations inutiles quand une opération asynchrone peut parfois être résolue immédiatement.

Les avantages sont l’optimisation des performantes dans le cas où le résultat est souvent disponible immédiatement, la réduction sur les accès au GC, ect…
Les inconvénients réside plus sur la complexité à gérer ces cas (une mauvaise utilisation pouvant entrainer des bugs). Par exemple pour l’exemple au dessus, quid si l’éléments n’est pas en cache ?
Comparatifs
| Aspect | Task<T> | ValueTask<T> |
|---|---|---|
| Type | Référence (class) | Valeur (struct) |
| Allocation | Toujours | Parfois évitable |
| Awaitable | Oui | Oui |
| Réutilisable | Oui | Non |
| Complexité | Faible | Moyenne à élevée |
| Scénarios recommandés | Opérations asynchrones | Résultats souvent synchrones |
Quand utiliser Task et ValueTask ?
| Scénario | À privilégier |
|---|---|
| Méthodes asynchrones généralistes | ✅ Task<T> |
| Méthodes qui retournent souvent un résultat mis en cache ou pré-calculé | ✅ ValueTask<T> |
| API publiques ou conception de bibliothèques | Task<T> |
| Chemins d’exécution critiques et hautement optimisés | ValueTask<T> |
| Code asynchrone susceptible d’être awaité plusieurs fois | Task<T> |
Méthodes généralistes : Task<T> est plus simple à utiliser et mieux supporté par les outils et bibliothèques. Il reste le choix par défaut pour la plupart des développeurs.
Résultats mis en cache : Si une méthode retourne souvent un résultat déjà disponible (comme une valeur en mémoire), ValueTask<T> permet d’éviter l’allocation d’un objet Task, ce qui améliore les performances.
API publiques : Pour les bibliothèques ou frameworks, Task<T> est recommandé car il est plus prévisible et évite les pièges liés à la réutilisation ou à la mauvaise gestion de ValueTask.
Chemins critiques : Dans des systèmes à haute performance (ex. : serveurs web, moteurs de base de données), ValueTask<T> peut réduire la pression sur le garbage collector en limitant les allocations.
Await multiple : ValueTask<T> ne peut être awaité qu’une seule fois, sauf si on le convertit en Task. Si ton code peut await plusieurs fois la même opération, reste sur Task<T>.