Asynchroon programmeermodel voor taken
U kunt knelpunten in de prestaties voorkomen en de algehele reactiesnelheid van uw toepassing verbeteren met behulp van asynchrone programmering. Traditionele technieken voor het schrijven van asynchrone toepassingen kunnen echter ingewikkeld zijn, waardoor ze moeilijk kunnen worden geschreven, fouten kunnen worden opgespoord en onderhouden.
C# biedt ondersteuning voor vereenvoudigde benadering, asynchrone programmering, die gebruikmaakt van asynchrone ondersteuning in de .NET-runtime. De compiler doet het moeilijke werk dat de ontwikkelaar heeft gebruikt en uw toepassing behoudt een logische structuur die lijkt op synchrone code. Als gevolg hiervan krijgt u alle voordelen van asynchrone programmering met een fractie van de inspanning.
Dit onderwerp bevat een overzicht van wanneer en hoe u asynchrone programmering gebruikt en koppelingen bevat naar ondersteuningsonderwerpen die details en voorbeelden bevatten.
Async verbetert de reactiesnelheid
Asynchroon is essentieel voor activiteiten die mogelijk blokkeren, zoals webtoegang. De toegang tot een webresource is soms traag of vertraagd. Als een dergelijke activiteit wordt geblokkeerd in een synchroon proces, moet de hele toepassing wachten. In een asynchroon proces kan de toepassing doorgaan met ander werk dat niet afhankelijk is van de webresource totdat de potentieel blokkerende taak is voltooid.
In de volgende tabel ziet u typische gebieden waarin asynchrone programmering de reactiesnelheid verbetert. De vermelde API's van .NET en Windows Runtime bevatten methoden die ondersteuning bieden voor asynchrone programmering.
Toepassingsgebied | .NET-typen met asynchrone methoden | Typen Windows Runtime met asynchrone methoden |
---|---|---|
Webtoegang | HttpClient | Windows.Web.Http.HttpClient SyndicationClient |
Werken met bestanden | JsonSerializer StreamReader StreamWriter XmlReader XmlWriter |
StorageFile |
Werken met afbeeldingen | MediaCapture BitmapEncoder BitmapDecoder |
|
WCF-programmering | Synchrone en asynchrone bewerkingen |
Asynchrony bewijst met name waardevol voor toepassingen die toegang hebben tot de UI-thread, omdat alle ui-gerelateerde activiteiten meestal één thread delen. Als een proces wordt geblokkeerd in een synchrone toepassing, worden alle processen geblokkeerd. Uw toepassing reageert niet meer en u kunt concluderen dat deze is mislukt wanneer de toepassing gewoon wacht.
Wanneer u asynchrone methoden gebruikt, blijft de toepassing reageren op de gebruikersinterface. U kunt bijvoorbeeld het formaat van een venster wijzigen of minimaliseren, of u kunt de toepassing sluiten als u niet wilt wachten totdat het is voltooid.
De asynchrone benadering voegt het equivalent van een automatische overdracht toe aan de lijst met opties waaruit u kunt kiezen bij het ontwerpen van asynchrone bewerkingen. Dat wil gezegd, u krijgt alle voordelen van traditionele asynchrone programmering, maar met veel minder inspanning van de ontwikkelaar.
Asynchrone methoden zijn eenvoudig te schrijven
De asynchrone trefwoorden in C# vormen het hart van asynchrone programmering. Met deze twee trefwoorden kunt u resources in .NET Framework, .NET Core of Windows Runtime gebruiken om bijna net zo eenvoudig een asynchrone methode te maken als u een synchrone methode maakt. Asynchrone methoden die u definieert met behulp van het async
trefwoord, worden asynchrone methoden genoemd.
In het volgende voorbeeld ziet u een asynchrone methode. Bijna alles in de code moet er bekend uitzien.
U vindt een volledig WPF-voorbeeld (Windows Presentation Foundation) dat u kunt downloaden van Asynchrone programmering met asynchrone programma's en wachten in 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...");
}
U kunt verschillende procedures uit het voorgaande voorbeeld leren. Begin met de methodehandtekening. Het bevat de async
wijzigingsfunctie. Het retourtype is Task<int>
(zie de sectie Retourtypen voor meer opties). De methodenaam eindigt in Async
. Retourneert in de hoofdtekst van de methode GetStringAsync
een Task<string>
. Dat betekent dat wanneer u await
de taak krijgt een string
(contents
). Voordat u op de taak wacht, kunt u werk uitvoeren dat niet afhankelijk is van de string
.GetStringAsync
Let goed op de await
operator. Het onderbreekt GetUrlContentLengthAsync
:
GetUrlContentLengthAsync
kan niet doorgaan totdatgetStringTask
het is voltooid.- Ondertussen keert het besturingselement terug naar de beller van
GetUrlContentLengthAsync
. - Hier wordt het besturingselement hervat wanneer
getStringTask
dit is voltooid. - De
await
operator haalt vervolgens hetstring
resultaat op vangetStringTask
.
De retourinstructie geeft een geheel getalresultaat op. Alle methoden die wachten GetUrlContentLengthAsync
op het ophalen van de lengtewaarde.
Als GetUrlContentLengthAsync
er geen werk is dat het kan doen tussen het aanroepen GetStringAsync
en wachten op de voltooiing ervan, kunt u uw code vereenvoudigen door aan te roepen en te wachten in de volgende enkele instructie.
string contents = await client.GetStringAsync("https://learn.microsoft.com/dotnet");
De volgende kenmerken geven een overzicht van wat het vorige voorbeeld een asynchrone methode maakt:
De methodehandtekening bevat een
async
wijzigingsfunctie.De naam van een asynchrone methode eindigt op conventie met een Async-achtervoegsel.
Het retourtype is een van de volgende typen:
- Task<TResult> als uw methode een retourinstructie heeft waarin de operand type
TResult
heeft. - Task als uw methode geen retourinstructie heeft of een retourinstructie zonder operand heeft.
void
als u een asynchrone gebeurtenis-handler schrijft.- Elk ander type dat een
GetAwaiter
methode heeft.
Zie de sectie Retourtypen en parameters voor meer informatie.
- Task<TResult> als uw methode een retourinstructie heeft waarin de operand type
De methode bevat meestal ten minste één
await
expressie, waarmee een punt wordt gemarkeerd waarop de methode niet kan worden voortgezet totdat de wachtende asynchrone bewerking is voltooid. Ondertussen wordt de methode onderbroken en wordt het besturingselement teruggezet naar de aanroeper van de methode. In de volgende sectie van dit onderwerp ziet u wat er op het schorsingspunt gebeurt.
In asynchrone methoden gebruikt u de opgegeven trefwoorden en typen om aan te geven wat u wilt doen, en de compiler doet de rest, inclusief het bijhouden van wat er moet gebeuren wanneer het besturingselement terugkeert naar een wachtpunt in een onderbroken methode. Sommige routineprocessen, zoals lussen en afhandeling van uitzonderingen, kunnen moeilijk te verwerken zijn in traditionele asynchrone code. In een asynchrone methode schrijft u deze elementen net zoals in een synchrone oplossing en wordt het probleem opgelost.
Zie TPL en traditionele .NET Framework asynchrone programmering voor meer informatie over asynchroon programmeren in eerdere versies van .NET Framework.
Wat gebeurt er in een asynchrone methode?
Het belangrijkste om te begrijpen in asynchrone programmering is hoe de controlestroom van methode naar methode wordt verplaatst. Het volgende diagram leidt u door het proces:
De getallen in het diagram komen overeen met de volgende stappen, geïnitieerd wanneer een aanroepmethode de asynchrone methode aanroept.
Een aanroepmethode roept aan en wacht op de
GetUrlContentLengthAsync
asynchrone methode.GetUrlContentLengthAsync
maakt een HttpClient exemplaar en roept de GetStringAsync asynchrone methode aan om de inhoud van een website als een tekenreeks te downloaden.Er gebeurt iets in
GetStringAsync
dat de voortgang ervan onderbreekt. Misschien moet er worden gewacht totdat een website is gedownload of een andere blokkeringsactiviteit. Om te voorkomen dat resources worden geblokkeerd,GetStringAsync
levert dit de controle over de aanroeperGetUrlContentLengthAsync
op.GetStringAsync
retourneert een Task<TResult>, waarbijTResult
een tekenreeks is enGetUrlContentLengthAsync
wijst de taak toe aan degetStringTask
variabele. De taak vertegenwoordigt het doorlopende proces voor de aanroep naarGetStringAsync
, met een toezegging om een werkelijke tekenreekswaarde te produceren wanneer het werk is voltooid.Omdat
getStringTask
nog niet is gewacht,GetUrlContentLengthAsync
kunt u doorgaan met ander werk dat niet afhankelijk is van het uiteindelijke resultaat vanGetStringAsync
. Dat werk wordt vertegenwoordigd door een aanroep naar de synchrone methodeDoIndependentWork
.DoIndependentWork
is een synchrone methode die het werk uitvoert en terugkeert naar de aanroeper.GetUrlContentLengthAsync
heeft geen werk meer dat het kan doen zonder een resultaat vangetStringTask
.GetUrlContentLengthAsync
vervolgens wil de lengte van de gedownloade tekenreeks berekenen en retourneren, maar de methode kan die waarde pas berekenen als de methode de tekenreeks heeft.GetUrlContentLengthAsync
Maakt daarom gebruik van een wachtoperator om de voortgang ervan op te schorten en de controle over te brengen naar de methode die wordt aangeroepenGetUrlContentLengthAsync
.GetUrlContentLengthAsync
retourneert eenTask<int>
aanroeper. De taak vertegenwoordigt een belofte om een geheel getal te produceren dat de lengte van de gedownloade tekenreeks is.Notitie
Als
GetStringAsync
(en daaromgetStringTask
) is voltooid voordatGetUrlContentLengthAsync
deze wordt gewacht, blijft de besturing inGetUrlContentLengthAsync
. De kosten van onderbreken en vervolgens terugkerenGetUrlContentLengthAsync
, worden verspild als het aangeroepen asynchrone procesgetStringTask
al is voltooid enGetUrlContentLengthAsync
niet hoeft te wachten op het uiteindelijke resultaat.Binnen de aanroepende methode wordt het verwerkingspatroon voortgezet. De beller kan ander werk doen dat niet afhankelijk is van het resultaat van
GetUrlContentLengthAsync
voordat hij dat resultaat wacht, of de beller kan onmiddellijk wachten. De aanroepmethode wacht opGetUrlContentLengthAsync
enGetUrlContentLengthAsync
wacht opGetStringAsync
.GetStringAsync
voltooit en produceert een tekenreeksresultaat. Het resultaat van de tekenreeks wordt niet geretourneerd door de aanroep naarGetStringAsync
de manier die u zou verwachten. (Houd er rekening mee dat de methode al een taak heeft geretourneerd in stap 3.) In plaats daarvan wordt het tekenreeksresultaat opgeslagen in de taak die de voltooiing van de methode aangeeft.getStringTask
De wachtoperator haalt het resultaat op vangetStringTask
. Met de toewijzingsinstructie wordt het opgehaalde resultaat toegewezen aancontents
.Wanneer
GetUrlContentLengthAsync
het tekenreeksresultaat is, kan de methode de lengte van de tekenreeks berekenen. Vervolgens is het werkGetUrlContentLengthAsync
voltooid en kan de wachtende gebeurtenis-handler hervatten. In het volledige voorbeeld aan het einde van het onderwerp kunt u bevestigen dat de gebeurtenis-handler de waarde van het lengteresultaat ophaalt en afdrukt. Neem even de tijd om rekening te houden met het verschil tussen synchroon en asynchroon gedrag als u geen asynchrone programmering hebt. Een synchrone methode retourneert wanneer het werk is voltooid (stap 5), maar een asynchrone methode retourneert een taakwaarde wanneer het werk wordt onderbroken (stap 3 en 6). Wanneer de asynchrone methode uiteindelijk het werk voltooit, wordt de taak gemarkeerd als voltooid en wordt het resultaat, indien aanwezig, opgeslagen in de taak.
Asynchrone API-methoden
Mogelijk vraagt u zich af waar u methoden kunt vinden, zoals GetStringAsync
die ondersteuning bieden voor asynchrone programmering. .NET Framework 4.5 of hoger en .NET Core bevatten veel leden waarmee en async
await
. U kunt deze herkennen door het achtervoegsel 'Async' dat is toegevoegd aan de naam van het lid en door het retourtype of TaskTask<TResult>. De System.IO.Stream
klasse bevat bijvoorbeeld methoden zoals CopyToAsync, ReadAsyncen WriteAsync naast de synchrone methoden CopyTo, Readen Write.
De Windows Runtime bevat ook veel methoden die u kunt gebruiken met async
en await
in Windows-apps. Zie Threading en asynchrone programmering voor UWP-ontwikkeling en Asynchrone programmering (Windows Store-apps) en quickstart: asynchrone API's aanroepen in C# of Visual Basic als u eerdere versies van Windows Runtime gebruikt.
Threads
Asynchrone methoden zijn bedoeld als niet-blokkerende bewerkingen. Een await
expressie in een asynchrone methode blokkeert de huidige thread niet terwijl de wachtende taak wordt uitgevoerd. In plaats daarvan registreert de expressie de rest van de methode als vervolg en retourneert het besturingselement naar de aanroeper van de asynchrone methode.
De async
trefwoorden en await
trefwoorden zorgen ervoor dat er geen extra threads worden gemaakt. Asynchrone methoden vereisen geen multithreading omdat een asynchrone methode niet wordt uitgevoerd op een eigen thread. De methode wordt uitgevoerd op de huidige synchronisatiecontext en gebruikt alleen tijd op de thread wanneer de methode actief is. U kunt Task.Run cpu-gebonden werk verplaatsen naar een achtergrondthread, maar een achtergrondthread helpt niet bij een proces dat alleen wacht tot er resultaten beschikbaar zijn.
De asynchrone benadering van asynchrone programmering verdient de voorkeur aan bestaande benaderingen in vrijwel elk geval. Deze benadering is met name beter dan de BackgroundWorker klasse voor I/O-gebonden bewerkingen, omdat de code eenvoudiger is en u niet hoeft te beschermen tegen racevoorwaarden. In combinatie met de Task.Run methode is asynchroon programmeren beter dan BackgroundWorker voor CPU-afhankelijke bewerkingen, omdat asynchrone programmering de coördinatiedetails van het uitvoeren van uw code scheidt van het werk dat Task.Run
wordt overgedragen naar de threadpool.
asynchroon en wachten
Als u opgeeft dat een methode een asynchrone methode is met behulp van de asynchrone wijziging, schakelt u de volgende twee mogelijkheden in.
De gemarkeerde asynchrone methode kan worden gebruikt om ophangpunten aan te wijzen. De
await
operator vertelt de compiler dat de asynchrone methode niet verder kan gaan dan dat punt totdat het wachtende asynchrone proces is voltooid. Ondertussen keert het besturingselement terug naar de aanroeper van de asynchrone methode.De schorsing van een asynchrone methode bij een
await
expressie vormt geen exit van de methode enfinally
blokken worden niet uitgevoerd.De gemarkeerde asynchrone methode kan zelf worden gewacht door methoden die deze aanroepen.
Een asynchrone methode bevat doorgaans een of meer exemplaren van een await
operator, maar het ontbreken van await
expressies veroorzaakt geen compilerfout. Als een asynchrone methode geen operator gebruikt await
om een veringspunt te markeren, wordt de methode uitgevoerd als een synchrone methode, ondanks de async
wijzigingsfunctie. De compiler geeft een waarschuwing voor dergelijke methoden uit.
async
en await
zijn contextuele trefwoorden. Zie de volgende onderwerpen voor meer informatie en voorbeelden:
Retourtypen en parameters
Een asynchrone methode retourneert meestal een Task of een Task<TResult>. Binnen een asynchrone methode wordt een await
operator toegepast op een taak die wordt geretourneerd vanuit een aanroep naar een andere asynchrone methode.
U geeft Task<TResult> op als het retourtype als de methode een return
instructie bevat waarmee een operand van het type TResult
wordt opgegeven.
U gebruikt Task als retourtype als de methode geen retourinstructie heeft of een retourinstructie heeft die geen operand retourneert.
U kunt ook elk ander retourtype opgeven, mits het type een GetAwaiter
methode bevat. ValueTask<TResult> is een voorbeeld van een dergelijk type. Het is beschikbaar in het NuGet-pakket System.Threading.Tasks.Extension .
In het volgende voorbeeld ziet u hoe u een methode declareert en aanroept die een Task<TResult> of meer Taskretourneert:
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();
Elke geretourneerde taak vertegenwoordigt doorlopend werk. Een taak bevat informatie over de status van het asynchrone proces en uiteindelijk het uiteindelijke resultaat van het proces of de uitzondering die het proces genereert als het niet lukt.
Een asynchrone methode kan ook een void
retourtype hebben. Dit retourtype wordt voornamelijk gebruikt om gebeurtenis-handlers te definiëren, waarbij een void
retourtype vereist is. Asynchrone gebeurtenis-handlers fungeren vaak als uitgangspunt voor asynchrone programma's.
Een asynchrone methode met een void
retourtype kan niet worden gewacht en de aanroeper van een ongeldige retourmethode kan geen uitzonderingen vangen die de methode genereert.
Een asynchrone methode kan niet declareren in parameters, maar de methode kan methoden aanroepen die dergelijke parameters hebben. Op dezelfde manier kan een asynchrone methode een waarde niet per verwijzing retourneren, hoewel deze methoden kan aanroepen met ref-retourwaarden.
Zie Asynchrone retourtypen (C#) voor meer informatie en voorbeelden.
Asynchrone API's in Windows Runtime-programmering hebben een van de volgende retourtypen, die vergelijkbaar zijn met taken:
- IAsyncOperation<TResult>, dat overeenkomt met Task<TResult>
- IAsyncAction, dat overeenkomt met Task
- IAsyncActionWithProgress<TProgress>
- IAsyncOperationWithProgress<TResult,TProgress>
Naamgevingsconventie
Volgens de conventie moeten methoden die vaak te verwachten typen retourneren (bijvoorbeeld Task
, Task<T>
ValueTask
, ValueTask<T>
) namen hebben die eindigen op 'Async'. Methoden die een asynchrone bewerking starten, maar geen verwachtbaar type retourneren, mogen geen namen hebben die eindigen op 'Async', maar kunnen beginnen met 'Begin', 'Start' of een ander werkwoord om aan te geven dat deze methode niet retourneert of het resultaat van de bewerking genereert.
U kunt de conventie negeren waarbij een gebeurtenis, basisklasse of interfacecontract een andere naam voorstelt. U moet bijvoorbeeld de naam van algemene gebeurtenishandlers niet wijzigen, zoals OnButtonClick
.
Verwante artikelen (Visual Studio)
Title | Beschrijving |
---|---|
Meerdere webaanvragen parallel maken met behulp van asynchroon en wachten (C#) | Demonstreert hoe u meerdere taken tegelijk start. |
Asynchrone retourtypen (C#) | Illustreert de typen die asynchrone methoden kunnen retourneren en legt uit wanneer elk type geschikt is. |
Annuleer taken met een annuleringstoken als signaleringsmechanisme. | Laat zien hoe u de volgende functionaliteit toevoegt aan uw asynchrone oplossing: - Een lijst met taken annuleren (C#) - Taken annuleren na een bepaalde periode (C#) - Asynchrone taak verwerken wanneer deze zijn voltooid (C#) |
Asynchroon gebruiken voor bestandstoegang (C#) | Lijsten en demonstreert de voordelen van het gebruik van asynchroon en wachten op toegang tot bestanden. |
Asynchroon patroon op basis van taken (TAP) | Beschrijft een asynchroon patroon, het patroon is gebaseerd op de Task en Task<TResult> typen. |
Async Video's op Channel 9 | Biedt koppelingen naar verschillende video's over asynchrone programmering. |