Delen via


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 totdat getStringTask 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 het string resultaat op van getStringTask.

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 TResultheeft.
    • 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.

  • 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:

Trace navigation of async control flow

De getallen in het diagram komen overeen met de volgende stappen, geïnitieerd wanneer een aanroepmethode de asynchrone methode aanroept.

  1. Een aanroepmethode roept aan en wacht op de GetUrlContentLengthAsync asynchrone methode.

  2. GetUrlContentLengthAsync maakt een HttpClient exemplaar en roept de GetStringAsync asynchrone methode aan om de inhoud van een website als een tekenreeks te downloaden.

  3. 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 aanroeper GetUrlContentLengthAsyncop.

    GetStringAsync retourneert een Task<TResult>, waarbij TResult een tekenreeks is en GetUrlContentLengthAsync wijst de taak toe aan de getStringTask variabele. De taak vertegenwoordigt het doorlopende proces voor de aanroep naar GetStringAsync, met een toezegging om een werkelijke tekenreekswaarde te produceren wanneer het werk is voltooid.

  4. Omdat getStringTask nog niet is gewacht, GetUrlContentLengthAsync kunt u doorgaan met ander werk dat niet afhankelijk is van het uiteindelijke resultaat van GetStringAsync. Dat werk wordt vertegenwoordigd door een aanroep naar de synchrone methode DoIndependentWork.

  5. DoIndependentWork is een synchrone methode die het werk uitvoert en terugkeert naar de aanroeper.

  6. GetUrlContentLengthAsync heeft geen werk meer dat het kan doen zonder een resultaat van getStringTask. 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 een Task<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 daarom getStringTask) is voltooid voordat GetUrlContentLengthAsync deze wordt gewacht, blijft de besturing in GetUrlContentLengthAsync. De kosten van onderbreken en vervolgens terugkeren GetUrlContentLengthAsync , worden verspild als het aangeroepen asynchrone proces getStringTask al is voltooid en GetUrlContentLengthAsync 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 op GetUrlContentLengthAsyncen GetUrlContentLengthAsync wacht op GetStringAsync.

  7. GetStringAsync voltooit en produceert een tekenreeksresultaat. Het resultaat van de tekenreeks wordt niet geretourneerd door de aanroep naar GetStringAsync 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 van getStringTask. Met de toewijzingsinstructie wordt het opgehaalde resultaat toegewezen aan contents.

  8. Wanneer GetUrlContentLengthAsync het tekenreeksresultaat is, kan de methode de lengte van de tekenreeks berekenen. Vervolgens is het werk GetUrlContentLengthAsync 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 asyncawait. 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 en finally 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 TResultwordt 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:

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.

Zie ook