ConfigureAwait et contexte de synchronisation
Comprenez SynchronizationContext, utilisez ConfigureAwait(false) dans le code des bibliothèques et évitez les interblocages dans les flux asynchrones.
ConfigureAwait et contexte de synchronisation est une leçon C# Academy gratuite sur CoddyKit. Ceci est la leçon 4 sur 4. Tu peux lire la leçon complète ci-dessous gratuitement — puis la pratiquer en direct dans le navigateur avec un éditeur de code intégré et un tuteur IA 24/7. Elle fait partie du parcours d'apprentissage C# Academy, et ta progression se synchronise sur le web et l'application CoddyKit. Le cours C# Academy comprend 4 leçons au total.
Qu’est-ce que SynchronizationContext ?
SynchronizationContext contrôle la façon dont le code de continuation après un await est planifié. Dans les applications d’interface utilisateur (WPF, WinForms), il garantit que les mises à jour de l’interface s’effectuent sur le thread de l’interface. Dans ASP.NET Core, il n’existe aucun contexte de synchronisation (il a été supprimé). Comprendre cela est essentiel pour écrire du code asynchrone correct.
Fonctionnement de ConfigureAwait
.ConfigureAwait(false) indique au mécanisme d’attente de NOT capturer le SynchronizationContext actuel ni d’y reprendre l’exécution. La continuation s’exécute plutôt sur n’importe quel fil d’exécution disponible du pool de fils d’exécution.
// Default: captures context (resumes on original context)
var data = await FetchDataAsync();
UpdateUI(data); // runs on UI thread — correct for UI apps
// ConfigureAwait(false): doesn't capture context
var data2 = await FetchDataAsync().ConfigureAwait(false);
// Runs on any thread — DON'T access UI thread-only objects hereLe schéma classique d’interblocage
Bloquer sur une Task avec .Result ou .Wait() tout en détenant un contexte de synchronisation provoque un interblocage classique : la continuation attend le contexte qui est bloqué en attendant la continuation.
// DEADLOCK in WPF/WinForms or ASP.NET classic:
string result = GetDataAsync().Result; // blocks UI thread
// GetDataAsync() tries to resume on UI thread after await
// UI thread is blocked by .Result
// DEADLOCK!
// Fix 1: await properly (never block)
string result = await GetDataAsync();
// Fix 2: use ConfigureAwait(false) in the library
public async Task<string> GetDataAsync()
=> await client.GetStringAsync(url).ConfigureAwait(false);ConfigureAwait(false) dans les bibliothèques
Le code d’une bibliothèque indépendant du contexte doit toujours utiliser ConfigureAwait(false) sur chaque attente. Cela empêche les interblocages lorsque les appelants utilisent la bibliothèque depuis un environnement sensible au contexte.
// Library code — always ConfigureAwait(false)
public async Task<User?> GetUserAsync(int id)
{
var json = await client
.GetStringAsync($"/users/{id}")
.ConfigureAwait(false);
var user = JsonSerializer.Deserialize<User>(json);
return user;
}
// Application code (has context) — leave ConfigureAwait default
public async Task LoadUserProfileAsync(int id)
{
var user = await userService.GetUserAsync(id); // context captured
ProfileLabel.Text = user?.Name; // safe to update UI
}ASP.NET Core : aucun SynchronizationContext
ASP.NET Core ne possède délibérément aucun SynchronizationContext. Les continuations s’exécutent dans le pool de fils d’exécution. ConfigureAwait(false) est techniquement sans effet, mais reste une bonne habitude pour le code partagé avec d’autres infrastructures.
// In ASP.NET Core, both are equivalent:
var data = await service.GetAsync(); // fine
var data2 = await service.GetAsync().ConfigureAwait(false); // also fine
// Context-free means: no deadlock risk from .Result in ASP.NET Core
// BUT: mixing with libraries that assume a context is still risky
// BEST PRACTICE: still use ConfigureAwait(false) in library codePool de fils d’exécution et planification par contexte
Sans contexte, les continuations sont planifiées par le TaskScheduler, généralement le ThreadPoolTaskScheduler. Avec un contexte d’interface utilisateur, elles sont envoyées dans la boucle de messages de l’interface utilisateur.
// Check current synchronization context:
Console.WriteLine(SynchronizationContext.Current?.GetType().Name
?? "No context (thread pool)");
// In a WPF event handler: "DispatcherSynchronizationContext"
// In ASP.NET Core: null (no context)
// In a unit test with xUnit: "AsyncTestSyncContext"
// After ConfigureAwait(false):
await Task.Delay(1).ConfigureAwait(false);
Console.WriteLine(SynchronizationContext.Current?.GetType().Name
?? "No context"); // null — context was abandonedConfigureAwait dans les boucles
Lorsqu’une attente est effectuée dans une boucle, la continuation de chaque itération s’exécute dans le contexte capturé, sauf si ConfigureAwait(false) est utilisé. Dans le code d’une bibliothèque, appliquez-le systématiquement à chaque attente de la boucle.
// Library loop: ConfigureAwait(false) on every await
public async Task ProcessBatchAsync(IEnumerable<int> ids)
{
foreach (var id in ids)
{
var item = await FetchItemAsync(id).ConfigureAwait(false);
await SaveItemAsync(item).ConfigureAwait(false);
}
}ValueTask et ConfigureAwait
ValueTask prend également en charge ConfigureAwait(false). Appliquez-le systématiquement selon les mêmes règles que pour Task.
public async ValueTask<string> GetFromCacheAsync(string key)
{
// ValueTask with ConfigureAwait(false)
var raw = await _store.GetAsync(key).ConfigureAwait(false);
return raw ?? "";
}Task.Run pour éviter le contexte
Utilisez Task.Run pour déporter les opérations gourmandes en calcul vers le pool de fils d’exécution, afin de garantir qu’elles ne s’exécutent jamais sur un fil détenant un contexte, comme celui de l’interface utilisateur.
// WRONG in UI app: blocks UI thread
void ButtonClick(object? sender, EventArgs e)
{
var result = HeavyComputation(); // blocks UI
Label.Text = result;
}
// CORRECT: offload to thread pool
async void ButtonClick(object? sender, EventArgs e)
{
var result = await Task.Run(() => HeavyComputation());
// Continuation captured UI context: safe to update UI
Label.Text = result;
}En pratique : bibliothèque HttpClient avec ConfigureAwait
Un wrapper HttpClient utilisé en production emploie ConfigureAwait(false) partout afin de pouvoir être appelé en toute sécurité depuis n’importe quel contexte.
public class ApiClient
{
private readonly HttpClient _http;
private readonly JsonSerializerOptions _opts = new(JsonSerializerDefaults.Web);
public ApiClient(HttpClient http) => _http = http;
public async Task<T> GetAsync<T>(string path, CancellationToken ct = default)
{
using var response = await _http
.GetAsync(path, ct)
.ConfigureAwait(false);
response.EnsureSuccessStatusCode();
return await response.Content
.ReadFromJsonAsync<T>(_opts, ct)
.ConfigureAwait(false)
?? throw new InvalidOperationException("Null response");
}
}Vérification rapide
Pourquoi l’appel de .Result sur une tâche dans une application WPF ou WinForms provoque-t-il souvent un interblocage ?
Récapitulatif : ConfigureAwait et SynchronizationContext
Points clés à retenir :
SynchronizationContext: planifie les continuations sur un fil précis, par exemple celui de l’interface utilisateurConfigureAwait(false): abandonne la capture du contexte et poursuit l’exécution dans le pool de fils d’exécution- Code de bibliothèque : utilisez toujours
ConfigureAwait(false)pour éviter les interblocages - Code d’application avec interface utilisateur : utilisez
awaitpar défaut pour revenir au fil de l’interface utilisateur - ASP.NET Core : aucun SynchronizationContext, mais cela reste une bonne pratique pour les bibliothèques partagées
- Ne bloquez jamais une
Taskavec.Result/.Wait()dans les applications sensibles au contexte
Apprends C# avec un tuteur IA — gratuit
Écris et exécute du vrai code dans ton navigateur, obtiens de l'aide instantanée d'un tuteur IA disponible 24h/24, et reprends là où tu t'es arrêté sur le web ou dans l'app.
- Cours
- 93
- Leçons
- 346
Questions Fréquemment Posées
La leçon « ConfigureAwait et contexte de synchronisation » est-elle gratuite ?
Oui — le texte complet de « ConfigureAwait et contexte de synchronisation » est gratuit à lire ici sur le web. Pour la pratiquer de manière interactive (un éditeur de code intégré et un tuteur IA 24/7) et déverrouiller le reste du cours C# Academy, passe à CoddyKit PRO. Le cours C# Academy comprend 4 leçons au total.
Qu'est-ce que j'apprendrai dans « ConfigureAwait et contexte de synchronisation » ?
Comprenez SynchronizationContext, utilisez ConfigureAwait(false) dans le code des bibliothèques et évitez les interblocages dans les flux asynchrones. Tu pratiques C# Academy avec du code pratique que tu exécutes directement dans le navigateur, et un tuteur IA 24/7 répond à tes questions au fur et à mesure que tu avances dans la leçon.
Dois-je avoir de l'expérience pour commencer C# Academy ?
Aucune expérience préalable n'est requise. C# Academy sur CoddyKit est structuré pour les débutants jusqu'aux apprenants avancés, donc tu peux commencer ici ou depuis le début et avancer à ton rythme. Ceci est la leçon 4 sur 4.
Combien de temps prend la leçon « ConfigureAwait et contexte de synchronisation » ?
La plupart des leçons CoddyKit prennent environ 5–10 minutes. Chacune est courte et interactive, tu progresses régulièrement et tu repiques exactement où tu t'es arrêté sur le web et l'app.
Peux-tu écrire et exécuter du code dans cette leçon C# Academy ?
Oui. Chaque leçon C# Academy inclut un éditeur de code intégré, tu écris et exécutes du vrai code directement dans ton navigateur et tu reçois des retours IA instantanés — aucune configuration locale requise.
Toutes les leçons de ce cours
- IAsyncEnumerable et await foreach
- System.Threading.Channels
- ValueTask et réduction des allocations
- ConfigureAwait et contexte de synchronisation