Dela via


Aktivitetsasynkron programmeringsmodell

Du kan undvika flaskhalsar i prestanda och förbättra programmets övergripande svarstider med hjälp av asynkron programmering. Traditionella tekniker för att skriva asynkrona program kan dock vara komplicerade, vilket gör dem svåra att skriva, felsöka och underhålla.

C# stöder förenklad metod, asynkron programmering, som utnyttjar asynkront stöd i .NET-körningen. Kompilatorn utför det svåra arbete som utvecklaren använde för att göra, och ditt program behåller en logisk struktur som liknar synkron kod. Därför får du alla fördelar med asynkron programmering med en bråkdel av arbetet.

Det här avsnittet innehåller en översikt över när och hur du använder asynkron programmering och innehåller länkar till supportavsnitt som innehåller information och exempel.

Async förbättrar svarstiden

Asynkron är viktigt för aktiviteter som potentiellt blockerar, till exempel webbåtkomst. Åtkomsten till en webbresurs är ibland långsam eller fördröjd. Om en sådan aktivitet blockeras i en synkron process måste hela programmet vänta. I en asynkron process kan programmet fortsätta med annat arbete som inte är beroende av webbresursen förrän den potentiellt blockerande uppgiften har slutförts.

Följande tabell visar typiska områden där asynkron programmering förbättrar svarstiden. De listade API:erna från .NET och Windows Runtime innehåller metoder som stöder asynkron programmering.

Programområde .NET-typer med asynkrona metoder Windows Runtime-typer med asynkrona metoder
Webbåtkomst HttpClient Windows.Web.Http.HttpClient
SyndicationClient
Arbeta med filer JsonSerializer
StreamReader
StreamWriter
XmlReader
XmlWriter
StorageFile
Arbeta med bilder MediaCapture
BitmapEncoder
BitmapDecoder
WCF-programmering Synkrona och asynkrona åtgärder

Asynkronhet visar sig vara särskilt värdefullt för program som har åtkomst till användargränssnittstråden eftersom all användargränssnittsrelaterad aktivitet vanligtvis delar en tråd. Om någon process blockeras i ett synkront program blockeras alla. Programmet slutar svara och du kan dra slutsatsen att det misslyckades när det i stället bara väntar.

När du använder asynkrona metoder fortsätter programmet att svara på användargränssnittet. Du kan till exempel ändra storlek på eller minimera ett fönster, eller stänga programmet om du inte vill vänta tills det har slutförts.

Den asynkrona metoden lägger till motsvarigheten till en automatisk överföring i listan över alternativ som du kan välja mellan när du utformar asynkrona åtgärder. Det innebär att du får alla fördelar med traditionell asynkron programmering men med mycket mindre arbete från utvecklaren.

Asynkrona metoder är enkla att skriva

Nyckelorden async och await i C# är kärnan i asynkron programmering. Genom att använda dessa två nyckelord kan du använda resurser i .NET Framework, .NET Core eller Windows Runtime för att skapa en asynkron metod nästan lika enkelt som du skapar en synkron metod. Asynkrona metoder som du definierar med nyckelordet async kallas asynkrona metoder.

I följande exempel visas en asynkron metod. Nästan allt i koden bör se bekant ut för dig.

Du hittar ett komplett WPF-exempel (Windows Presentation Foundation) som kan laddas ned från asynkron programmering med asynkron asynkron programmering och väntar i C#.

public async Task<int> GetUrlContentLengthAsync()
{
    using var client = new HttpClient();

    Task<string> getStringTask =
        client.GetStringAsync("https://learn.microsoft.com/dotnet");

    DoIndependentWork();

    string contents = await getStringTask;

    return contents.Length;
}

void DoIndependentWork()
{
    Console.WriteLine("Working...");
}

Du kan lära dig flera metoder från föregående exempel. Börja med metodsignaturen. Den innehåller async modifieraren. Returtypen är Task<int> (se avsnittet "Returtyper" för fler alternativ). Metodnamnet slutar i Async. I metodens GetStringAsync brödtext returnerar en Task<string>. Det innebär att när du await får uppgiften får du en string (contents). Innan du väntar på uppgiften kan du utföra arbete som inte förlitar sig på string från GetStringAsync.

Var uppmärksam på operatorn await . Den pausar GetUrlContentLengthAsync:

  • GetUrlContentLengthAsync kan inte fortsätta förrän getStringTask den är klar.
  • Under tiden återgår kontrollen till anroparen för GetUrlContentLengthAsync.
  • Kontrollen återupptas här när getStringTask den är klar.
  • Operatorn await hämtar sedan resultatet string från getStringTask.

Retursatsen anger ett heltalsresultat. Alla metoder som väntar på GetUrlContentLengthAsync att hämta längdvärdet.

Om GetUrlContentLengthAsync det inte finns något arbete som kan utföras mellan att anropa GetStringAsync och vänta på att den ska slutföras, kan du förenkla koden genom att anropa och vänta i följande enda instruktion.

string contents = await client.GetStringAsync("https://learn.microsoft.com/dotnet");

Följande egenskaper sammanfattar vad som gör föregående exempel till en asynkron metod:

  • Metodsignaturen innehåller en async modifierare.

  • Namnet på en asynkron metod, enligt konvention, slutar med suffixet "Async".

  • Returtypen är en av följande typer:

    • Task<TResult> om metoden har en retursats där operanden har typen TResult.
    • Task om metoden inte har någon retursats eller har en retursats utan operand.
    • void om du skriver en asynkron händelsehanterare.
    • Alla andra typer som har en GetAwaiter metod.

    Mer information finns i avsnittet Returtyper och parametrar .

  • Metoden innehåller vanligtvis minst ett await uttryck, vilket markerar en punkt där metoden inte kan fortsätta förrän den väntade asynkrona åtgärden har slutförts. Under tiden pausas metoden och kontrollen återgår till metodens anropare. Nästa avsnitt i det här avsnittet visar vad som händer vid avstängningspunkten.

I asynkrona metoder använder du de angivna nyckelorden och typerna för att ange vad du vill göra, och kompilatorn gör resten, inklusive att hålla reda på vad som måste hända när kontrollen återgår till en väntepunkt i en pausad metod. Vissa rutinprocesser, till exempel loopar och undantagshantering, kan vara svåra att hantera i traditionell asynkron kod. I en asynkron metod skriver du dessa element ungefär som du skulle göra i en synkron lösning, och problemet är löst.

Mer information om asynkron i tidigare versioner av .NET Framework finns i TPL och traditionell .NET Framework-asynkron programmering.

Vad händer i en asynkron metod

Det viktigaste att förstå i asynkron programmering är hur kontrollflödet flyttas från metod till metod. Följande diagram leder dig genom processen:

Trace navigation of async control flow

Siffrorna i diagrammet motsvarar följande steg som initieras när en anropande metod anropar metoden async.

  1. En anropande metod anropar och väntar på asynkron GetUrlContentLengthAsync metod.

  2. GetUrlContentLengthAsync skapar en HttpClient instans och anropar den GetStringAsync asynkrona metoden för att ladda ned innehållet på en webbplats som en sträng.

  3. Något händer i GetStringAsync som pausar dess framsteg. Kanske måste den vänta tills en webbplats laddas ned eller någon annan blockerande aktivitet. För att undvika att blockera resurser GetStringAsync ger kontroll till anroparen, GetUrlContentLengthAsync.

    GetStringAsync returnerar en Task<TResult>, där TResult är en sträng och GetUrlContentLengthAsync tilldelar aktiviteten till variabeln getStringTask . Uppgiften representerar den pågående processen för anropet till GetStringAsync, med ett åtagande att skapa ett verkligt strängvärde när arbetet är klart.

  4. Eftersom getStringTask du inte har väntat ännu GetUrlContentLengthAsync kan du fortsätta med annat arbete som inte är beroende av slutresultatet från GetStringAsync. Det arbetet representeras av ett anrop till den synkrona metoden DoIndependentWork.

  5. DoIndependentWork är en synkron metod som utför sitt arbete och återgår till anroparen.

  6. GetUrlContentLengthAsync har slut på arbete som det kan göra utan ett resultat från getStringTask. GetUrlContentLengthAsync vill sedan beräkna och returnera längden på den nedladdade strängen, men metoden kan inte beräkna det värdet förrän metoden har strängen.

    GetUrlContentLengthAsync Därför använder en await-operator för att pausa förloppet och för att ge kontroll till metoden som anropade GetUrlContentLengthAsync. GetUrlContentLengthAsync returnerar en Task<int> till anroparen. Uppgiften representerar ett löfte om att skapa ett heltalsresultat som är längden på den nedladdade strängen.

    Kommentar

    Om GetStringAsync (och därför getStringTask) slutförs innan GetUrlContentLengthAsync det väntar, förblir kontrollen i GetUrlContentLengthAsync. Kostnaden för att pausa och sedan återgå till GetUrlContentLengthAsync skulle slösas bort om den anropade asynkrona processen getStringTask redan har slutförts och GetUrlContentLengthAsync inte behöver vänta på slutresultatet.

    I anropsmetoden fortsätter bearbetningsmönstret. Anroparen kan utföra annat arbete som inte är beroende av resultatet från GetUrlContentLengthAsync innan du väntar på det resultatet, eller så kan anroparen vänta omedelbart. Anropsmetoden väntar GetUrlContentLengthAsyncpå och GetUrlContentLengthAsync väntar på GetStringAsync.

  7. GetStringAsync slutför och genererar ett strängresultat. Strängresultatet returneras inte av anropet till GetStringAsync på det sätt som du kan förvänta dig. (Kom ihåg att metoden redan returnerade en uppgift i steg 3.) I stället lagras strängresultatet i uppgiften som representerar slutförandet av metoden , getStringTask. Await-operatorn hämtar resultatet från getStringTask. Tilldelningsinstruktor tilldelar det hämtade resultatet till contents.

  8. När GetUrlContentLengthAsync har strängresultatet kan metoden beräkna längden på strängen. Sedan är arbetet GetUrlContentLengthAsync med också slutfört och den väntande händelsehanteraren kan återupptas. I det fullständiga exemplet i slutet av ämnet kan du bekräfta att händelsehanteraren hämtar och skriver ut värdet för längdresultatet. Om du är nybörjare på asynkron programmering tar det en minut att tänka på skillnaden mellan synkront och asynkront beteende. En synkron metod returnerar när dess arbete är klart (steg 5), men en asynkron metod returnerar ett aktivitetsvärde när dess arbete pausas (steg 3 och 6). När asynkron metoden så småningom slutför sitt arbete markeras uppgiften som slutförd och resultatet, om det finns något, lagras i aktiviteten.

API-asynkrona metoder

Du kanske undrar var du hittar metoder som den som GetStringAsync stöder asynkron programmering. .NET Framework 4.5 eller senare och .NET Core innehåller många medlemmar som arbetar med async och await. Du kan känna igen dem med suffixet "Async" som läggs till i medlemsnamnet och av deras returtyp eller TaskTask<TResult>. Klassen innehåller till exempel System.IO.Stream metoder som CopyToAsync, ReadAsyncoch WriteAsync tillsammans med synkrona metoder CopyTo, Readoch Write.

Windows Runtime innehåller också många metoder som du kan använda med async och await i Windows-appar. Mer information finns i Trådning och asynkron programmering för UWP-utveckling, asynkron programmering (Windows Store-appar) och Snabbstart: Anropa asynkrona API:er i C# eller Visual Basic om du använder tidigare versioner av Windows Runtime.

Trådar

Asynkrona metoder är avsedda att vara icke-blockerande åtgärder. Ett await uttryck i en asynkron metod blockerar inte den aktuella tråden medan den väntade aktiviteten körs. I stället registrerar uttrycket resten av metoden som en fortsättning och returnerar kontrollen till anroparen för async-metoden.

Nyckelorden async och await orsakar inte att ytterligare trådar skapas. Asynkrona metoder kräver inte multitrådning eftersom en asynkron metod inte körs på en egen tråd. Metoden körs i den aktuella synkroniseringskontexten och använder endast tid i tråden när metoden är aktiv. Du kan använda Task.Run för att flytta cpu-bundet arbete till en bakgrundstråd, men en bakgrundstråd hjälper inte till med en process som bara väntar på att resultaten ska bli tillgängliga.

Den asynkrona metoden för asynkron programmering är att föredra framför befintliga metoder i nästan alla fall. I synnerhet är den här metoden bättre än BackgroundWorker klassen för I/O-bundna åtgärder eftersom koden är enklare och du inte behöver skydda dig mot konkurrensförhållanden. I kombination med Task.Run metoden är asynkron programmering bättre än BackgroundWorker för CPU-bundna åtgärder eftersom asynkron programmering skiljer samordningsinformationen för att köra koden från det arbete som Task.Run överförs till trådpoolen.

async och await

Om du anger att en metod är en asynkron metod med hjälp av asynkron modifieraren aktiverar du följande två funktioner.

  • Den markerade asynkrona metoden kan använda await för att ange upphängningspunkter. Operatorn await meddelar kompilatorn att asynkron metod inte kan fortsätta förbi den punkten förrän den väntade asynkrona processen är klar. Under tiden återgår kontrollen till anroparen för async-metoden.

    Avstängningen av en asynkron metod vid ett await uttryck utgör inte ett avslut från metoden och finally block körs inte.

  • Den markerade asynkrona metoden kan i sig inväntas med metoder som anropar den.

En asynkron metod innehåller vanligtvis en eller flera förekomster av en await operator, men avsaknaden av await uttryck orsakar inte ett kompilatorfel. Om en asynkron metod inte använder en await operator för att markera en fjädringspunkt körs metoden som en synkron metod gör, trots async modifieraren. Kompilatorn utfärdar en varning för sådana metoder.

async och await är kontextuella nyckelord. Mer information och exempel finns i följande avsnitt:

Returnera typer och parametrar

En asynkron metod returnerar vanligtvis en Task eller en Task<TResult>. I en asynkron metod tillämpas en await operator på en aktivitet som returneras från ett anrop till en annan asynkron metod.

Du anger Task<TResult> som returtyp om metoden innehåller en return instruktion som anger en operand av typen TResult.

Du använder Task som returtyp om metoden inte har någon retursats eller har en retursats som inte returnerar en operand.

Du kan också ange vilken annan returtyp som helst, förutsatt att typen innehåller en GetAwaiter metod. ValueTask<TResult> är ett exempel på en sådan typ. Den är tillgänglig i NuGet-paketet System.Threading.Tasks.Extension .

I följande exempel visas hur du deklarerar och anropar en metod som returnerar en Task<TResult> eller ett Task:

async Task<int> GetTaskOfTResultAsync()
{
    int hours = 0;
    await Task.Delay(0);

    return hours;
}


Task<int> returnedTaskTResult = GetTaskOfTResultAsync();
int intResult = await returnedTaskTResult;
// Single line
// int intResult = await GetTaskOfTResultAsync();

async Task GetTaskAsync()
{
    await Task.Delay(0);
    // No return statement needed
}

Task returnedTask = GetTaskAsync();
await returnedTask;
// Single line
await GetTaskAsync();

Varje returnerad uppgift representerar pågående arbete. En uppgift kapslar in information om tillståndet för den asynkrona processen och slutligen antingen det slutliga resultatet från processen eller undantaget som processen genererar om den inte lyckas.

En asynkron metod kan också ha en void returtyp. Den här returtypen används främst för att definiera händelsehanterare, där en void returtyp krävs. Asynkrona händelsehanterare fungerar ofta som startpunkt för asynkrona program.

Det går inte att vänta på en asynkron metod som har en void returtyp, och anroparen för en void-returning-metod kan inte fånga upp några undantag som metoden genererar.

En asynkron metod kan inte deklarera in- eller utdataparametrar, men metoden kan anropa metoder som har sådana parametrar. På samma sätt kan en asynkron metod inte returnera ett värde med referens, även om den kan anropa metoder med referensreturvärden.

Mer information och exempel finns i Async return types (C#).

Asynkrona API:er i Windows Runtime-programmering har någon av följande returtyper, som liknar uppgifter:

Namngivningskonvention

Enligt konventionen bör metoder som returnerar ofta väntande typer (till exempel Task, Task<T>, ValueTask, ValueTask<T>) ha namn som slutar med "Async". Metoder som startar en asynkron åtgärd men inte returnerar en väntande typ bör inte ha namn som slutar med "Async", men kan börja med "Begin", "Start" eller något annat verb som tyder på att den här metoden inte returnerar eller genererar resultatet av åtgärden.

Du kan ignorera konventionen där en händelse, basklass eller ett gränssnittskontrakt föreslår ett annat namn. Du bör till exempel inte byta namn på vanliga händelsehanterare, till exempel OnButtonClick.

Relaterade artiklar (Visual Studio)

Title Description
Så här gör du flera webbbegäranden parallellt med hjälp av async and await (C#) Visar hur du startar flera uppgifter samtidigt.
Async-returtyper (C#) Visar de typer som asynkrona metoder kan returnera och förklarar när varje typ är lämplig.
Avbryt aktiviteter med en annulleringstoken som en signalmekanism. Visar hur du lägger till följande funktioner i din asynkrona lösning:

- Avbryt en lista över uppgifter (C#)
- Avbryt aktiviteter efter en tidsperiod (C#)
- Bearbeta asynkrona uppgifter när de slutförs (C#)
Använda async för filåtkomst (C#) Visar och visar fördelarna med att använda asynkronisering och väntar på att få åtkomst till filer.
Aktivitetsbaserat asynkront mönster (TAP) Beskriver ett asynkront mönster, mönstret baseras på typerna Task och Task<TResult> .
Async-videor på Channel 9 Innehåller länkar till en mängd olika videor om asynkron programmering.

Se även