C# Academy · Lektion

ConfigureAwait och synkroniseringskontext

Förstå SynchronizationContext, använd ConfigureAwait(false) i bibliotekskod och undvik deadlocks i asynkrona flöden.

Lektion 4 av 412 steg

ConfigureAwait och synkroniseringskontext är en gratis lektion i C# Academy på CoddyKit. Detta är lektion 4 av 4. Ni kan läsa hela lektionen gratis nedan och sedan öva praktiskt i webbläsaren med en inbyggd kodredigerare och en AI-handledare som är tillgänglig dygnet runt. Den ingår i lärvägen för C# Academy, och Era framsteg synkroniseras mellan webben och CoddyKit-appen. Kursen i C# Academy innehåller totalt 4 lektioner.

Vad är SynchronizationContext?

SynchronizationContext styr hur fortsättningskoden efter ett await schemaläggs. I UI-appar (WPF, WinForms) säkerställer den att UI-uppdateringar sker på UI-tråden. I ASP.NET Core finns ingen synkroniseringskontext (den togs bort). Att förstå detta är viktigt för att skriva korrekt asynkron kod.

Så fungerar ConfigureAwait

.ConfigureAwait(false) anger för awaitern att den INTE ska fånga och återuppta körningen på den aktuella SynchronizationContext. Fortsättningen körs i stället på en valfri tillgänglig tråd från trådpoolen.

// 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 here

Det klassiska deadlock-mönstret

Att blockera på en Task med .Result eller .Wait() medan en synkroniseringskontext hålls låst orsakar ett klassiskt deadlock — fortsättningen väntar på kontexten som är blockerad i väntan på fortsättningen.

// 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) i bibliotek

Bibliotekskod som inte är beroende av någon kontext bör alltid använda ConfigureAwait(false) vid varje await. Det förhindrar deadlock när anropare använder biblioteket från en kontextkänslig miljö.

// 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: Ingen SynchronizationContext

ASP.NET Core har medvetet ingen SynchronizationContext. Fortsättningar körs i trådpoolen. ConfigureAwait(false) är tekniskt sett en no-op, men är fortfarande en bra vana för kod som delas med andra ramverk.

// 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 code

Trådpool kontra kontextschemaläggning

Utan en kontext schemaläggs fortsättningar av TaskScheduler — vanligtvis ThreadPoolTaskScheduler. Med en UI-kontext läggs de i stället på meddelandeloopen för användargränssnittet.

// 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 abandoned

ConfigureAwait i loopar

När await används i en loop körs varje iterations fortsättning i den fångade kontexten, såvida inte ConfigureAwait(false) används. I bibliotekskod bör det användas konsekvent vid varje await i loopen.

// 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 och ConfigureAwait

ValueTask stöder också ConfigureAwait(false). Använd det konsekvent enligt samma regler som för Task.

public async ValueTask<string> GetFromCacheAsync(string key)
{
    // ValueTask with ConfigureAwait(false)
    var raw = await _store.GetAsync(key).ConfigureAwait(false);
    return raw ?? "";
}

Task.Run för att undvika kontextfångst

Använd Task.Run för att flytta CPU-bundet arbete till trådpoolen och säkerställa att det aldrig körs på en tråd som håller en kontext, till exempel UI-tråden.

// 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;
}

I praktiken: HttpClient-bibliotek med ConfigureAwait

Ett HTTP-klientomslag i produktionskod använder ConfigureAwait(false) konsekvent för att vara säkert vid anrop från alla kontexter.

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");
    }
}

Snabbkontroll

Varför orsakar ett anrop till .Result på en Task ofta ett deadlock i en WPF- eller WinForms-app?

Sammanfattning: ConfigureAwait och SynchronizationContext

Viktiga lärdomar:

  • SynchronizationContext: schemalägger fortsättningar tillbaka till en specifik tråd, till exempel UI-tråden
  • ConfigureAwait(false): avstår från kontextfångst och fortsätter i trådpoolen
  • Bibliotekskod: använd alltid ConfigureAwait(false) för att förhindra deadlock
  • Applikationskod med UI: använd standardbeteendet hos await för att återgå till UI-tråden
  • ASP.NET Core: ingen SynchronizationContext — men det är fortfarande god praxis för delade bibliotek
  • Blockera aldrig på en Task med .Result/.Wait() i kontextkänsliga appar
Gratis att börja

Lär dig C# med en AI-lärare – gratis

Skriv och kör riktig kod i webbläsaren, få omedelbar hjälp av en AI-lärare dygnet runt och fortsätt där du slutade – på webben eller i appen.

Kurser
93
Lektioner
346

Vanliga frågor

Är lektionen ”ConfigureAwait och synkroniseringskontext” gratis?

Ja – hela texten till ”ConfigureAwait och synkroniseringskontext” kan läsas gratis här på webben. Om Ni vill öva interaktivt med en inbyggd kodredigerare och en AI-handledare som är tillgänglig dygnet runt och låsa upp resten av kursen i C# Academy, kan Ni uppgradera till CoddyKit PRO. Kursen i C# Academy innehåller totalt 4 lektioner.

Vad lär jag mig i ”ConfigureAwait och synkroniseringskontext”?

Förstå SynchronizationContext, använd ConfigureAwait(false) i bibliotekskod och undvik deadlocks i asynkrona flöden. Ni övar på C# Academy med praktisk kod som körs direkt i webbläsaren, medan en AI-handledare som är tillgänglig dygnet runt svarar på Era frågor under lektionen.

Behöver jag någon erfarenhet för att börja lära mig C# Academy?

Du behöver inga förkunskaper. Utbildningen i C# Academy på CoddyKit är upplagd för allt från nybörjare till avancerade elever, så att du kan börja här eller från början och gå fram i din egen takt. Detta är lektion 4 av 4.

Hur lång tid tar lektionen ”ConfigureAwait och synkroniseringskontext”?

De flesta CoddyKit-lektioner tar cirka 5–10 minuter. Varje lektion är kort och interaktiv, så att du gör stadiga framsteg och kan fortsätta precis där du slutade – på webben eller i appen.

Kan jag skriva och köra kod i den här C# Academy-lektionen?

Ja. Varje C# Academy-lektion innehåller en inbyggd kodredigerare, så att du kan skriva och köra riktig kod direkt i webbläsaren och få omedelbar AI-feedback – utan lokal installation.

Alla lektioner i den här kursen

  1. IAsyncEnumerable och await foreach
  2. System.Threading.Channels
  3. ValueTask och undvika allokeringar
  4. ConfigureAwait och synkroniseringskontext
← Tillbaka till C# Academy