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://video2.skills-academy.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ängetStringTask
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 resultatetstring
frångetStringTask
.
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://video2.skills-academy.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 .
- Task<TResult> om metoden har en retursats där operanden har typen
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:
Siffrorna i diagrammet motsvarar följande steg som initieras när en anropande metod anropar metoden async.
En anropande metod anropar och väntar på asynkron
GetUrlContentLengthAsync
metod.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.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 resurserGetStringAsync
ger kontroll till anroparen,GetUrlContentLengthAsync
.GetStringAsync
returnerar en Task<TResult>, därTResult
är en sträng ochGetUrlContentLengthAsync
tilldelar aktiviteten till variabelngetStringTask
. Uppgiften representerar den pågående processen för anropet tillGetStringAsync
, med ett åtagande att skapa ett verkligt strängvärde när arbetet är klart.Eftersom
getStringTask
du inte har väntat ännuGetUrlContentLengthAsync
kan du fortsätta med annat arbete som inte är beroende av slutresultatet frånGetStringAsync
. Det arbetet representeras av ett anrop till den synkrona metodenDoIndependentWork
.DoIndependentWork
är en synkron metod som utför sitt arbete och återgår till anroparen.GetUrlContentLengthAsync
har slut på arbete som det kan göra utan ett resultat frångetStringTask
.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 anropadeGetUrlContentLengthAsync
.GetUrlContentLengthAsync
returnerar enTask<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örgetStringTask
) slutförs innanGetUrlContentLengthAsync
det väntar, förblir kontrollen iGetUrlContentLengthAsync
. Kostnaden för att pausa och sedan återgå tillGetUrlContentLengthAsync
skulle slösas bort om den anropade asynkrona processengetStringTask
redan har slutförts ochGetUrlContentLengthAsync
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äntarGetUrlContentLengthAsync
på ochGetUrlContentLengthAsync
väntar påGetStringAsync
.GetStringAsync
slutför och genererar ett strängresultat. Strängresultatet returneras inte av anropet tillGetStringAsync
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ångetStringTask
. Tilldelningsinstruktor tilldelar det hämtade resultatet tillcontents
.När
GetUrlContentLengthAsync
har strängresultatet kan metoden beräkna längden på strängen. Sedan är arbetetGetUrlContentLengthAsync
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 ochfinally
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:
- IAsyncOperation<TResult>, vilket motsvarar Task<TResult>
- IAsyncAction, vilket motsvarar Task
- IAsyncActionWithProgress<TProgress>
- IAsyncOperationWithProgress<TResult,TProgress>
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. |