Notitie
Voor toegang tot deze pagina is autorisatie vereist. U kunt proberen u aan te melden of de directory te wijzigen.
Voor toegang tot deze pagina is autorisatie vereist. U kunt proberen de mappen te wijzigen.
In .NET is het op taken gebaseerde asynchrone patroon het aanbevolen asynchrone ontwerppatroon voor nieuwe ontwikkeling. Deze is gebaseerd op de Task en Task<TResult> typen in de System.Threading.Tasks naamruimte, die worden gebruikt om asynchrone bewerkingen weer te geven.
Naamgeving, parameters en retourtypen
TAP gebruikt één methode om de start en voltooiing van een asynchrone bewerking aan te geven. Dit contrasteert met zowel het Asynchrone programmeermodelpatroon (APM of IAsyncResult
) als het op gebeurtenissen gebaseerde Asynchrone patroon (EAP). APM vereist Begin
en End
methoden. EAP vereist een methode met het Async
-achtervoegsel en daarnaast vereist het een of meerdere gebeurtenissen, delegatetypen voor gebeurtenishandlers en EventArg
-afgeleide typen. Asynchrone methoden in TAP bevatten het Async
achtervoegsel na de naam van de bewerking voor methoden die te verwachten typen retourneren, zoals Task, Task<TResult>, ValueTasken ValueTask<TResult>. Een asynchrone Get
bewerking die een Task<String>
retourneert, kan bijvoorbeeld GetAsync
heten. Als u een TAP-methode toevoegt aan een klasse die al een EAP-methodenaam met het Async
achtervoegsel bevat, gebruikt u in plaats daarvan het achtervoegsel TaskAsync
. Als de klasse bijvoorbeeld al een GetAsync
methode heeft, gebruikt u de naam GetTaskAsync
. Als een methode een asynchrone bewerking start, maar geen wachtbaar type retourneert, moet de naam beginnen met Begin
, Start
of een ander werkwoord om aan te geven dat deze methode het resultaat van de bewerking niet retourneert of genereert.
Een TAP-methode retourneert een System.Threading.Tasks.Task of een System.Threading.Tasks.Task<TResult>, afhankelijk van of de bijbehorende synchrone methode void of een type TResult
retourneert.
De parameters van een TAP-methode moeten overeenkomen met de parameters van de synchrone tegenhanger en moeten in dezelfde volgorde worden opgegeven. Parameters out
en ref
vallen echter buiten deze regel en moeten volledig vermeden worden. Alle gegevens die via een out
- of ref
-parameter zouden zijn geretourneerd, moeten in plaats daarvan worden geretourneerd als onderdeel van de TResult
die door Task<TResult> wordt geretourneerd, en ze moeten een tuple of een aangepaste gegevensstructuur gebruiken om meerdere waarden te bevatten. Overweeg ook om een CancellationToken parameter toe te voegen, zelfs als de synchrone tegenhanger van de TAP-methode er geen biedt.
Methoden die uitsluitend zijn gewijd aan het maken, bewerken of combineren van taken (waarbij de asynchrone intentie van de methode duidelijk is in de naam van de methode of in de naam van het type waartoe de methode behoort), hoeven dit naamgevingspatroon niet te volgen; dergelijke methoden worden vaak combinaties genoemd. Voorbeelden van combinatoren zijn onder andere WhenAll en WhenAny, en worden besproken in de sectie 'Het gebruiken van ingebouwde, taak-gebaseerde combinatoren' van het artikel 'Het gebruik van het taak-gebaseerde asynchrone patroon'.
Zie Asynchrone programmeerpatronen voor voorbeelden van hoe de TAP-syntaxis verschilt van de syntaxis die wordt gebruikt in verouderde asynchrone programmeerpatronen, zoals het Asynchrone programmeermodel (APM) en het op gebeurtenissen gebaseerde Asynchrone patroon (EAP).
Een asynchrone bewerking starten
Een asynchrone methode die is gebaseerd op TAP, kan een kleine hoeveelheid werk synchroon uitvoeren, zoals het valideren van argumenten en het initiëren van de asynchrone bewerking, voordat de resulterende taak wordt geretourneerd. Synchroon werk moet tot een minimum worden beperkt zodat de asynchrone methode snel kan terugkeren. Redenen voor een snelle terugkeer zijn onder andere:
Asynchrone methoden kunnen worden aangeroepen vanuit ui-threads (user interface) en elk langlopend synchroon werk kan de reactiesnelheid van de toepassing schaden.
Meerdere asynchrone methoden kunnen gelijktijdig worden gestart. Daarom kan elk langlopend werk in het synchrone gedeelte van een asynchrone methode de start van andere asynchrone bewerkingen vertragen, waardoor de voordelen van gelijktijdigheid afnemen.
In sommige gevallen is de hoeveelheid werk die nodig is om de bewerking te voltooien, kleiner dan de hoeveelheid werk die nodig is om de bewerking asynchroon te starten. Lezen vanuit een stroom waarbij aan de leesbewerking kan worden voldaan door gegevens die al in het geheugen zijn gebufferd, is een voorbeeld van een dergelijk scenario. In dergelijke gevallen kan de bewerking synchroon worden voltooid en kan een taak worden geretourneerd die al is voltooid.
Uitzonderingen
Een asynchrone methode moet een uitzondering genereren die alleen wordt gegenereerd uit de asynchrone methodeaanroep als reactie op een gebruiksfout. Gebruiksfouten mogen nooit optreden in productiecode. Als u bijvoorbeeld een null-verwijzing (Nothing
in Visual Basic) doorgeeft als een van de argumenten van de methode een foutstatus veroorzaakt (meestal vertegenwoordigd door een ArgumentNullException uitzondering), kunt u de aanroepende code wijzigen om ervoor te zorgen dat er nooit een null-verwijzing wordt doorgegeven. Voor alle andere fouten moeten uitzonderingen die optreden wanneer een asynchrone methode wordt uitgevoerd, worden toegewezen aan de geretourneerde taak, zelfs als de asynchrone methode synchroon wordt uitgevoerd voordat de taak wordt geretourneerd. Normaal gesproken bevat een taak maximaal één uitzondering. Als de taak echter meerdere bewerkingen vertegenwoordigt (bijvoorbeeld WhenAll), kunnen er meerdere uitzonderingen aan één taak worden gekoppeld.
Doelomgeving
Wanneer u een TAP-methode implementeert, kunt u bepalen waar asynchrone uitvoering plaatsvindt. U kunt ervoor kiezen om de workload in de threadgroep uit te voeren, deze te implementeren met behulp van asynchrone I/O (zonder dat deze is gebonden aan een thread voor het merendeel van de uitvoering van de bewerking), deze uit te voeren op een specifieke thread (zoals de UI-thread) of een willekeurig aantal mogelijke contexten te gebruiken. Een TAP-methode kan zelfs niets hebben om uit te voeren en kan alleen een Task gebeurtenis retourneren die het optreden van een voorwaarde ergens anders in het systeem vertegenwoordigt (bijvoorbeeld een taak die gegevens vertegenwoordigt die in een gegevensstructuur in de wachtrij terechtkomt).
De aanroeper van de TAP-methode kan het wachten blokkeren totdat de TAP-methode is voltooid door synchroon te wachten op de resulterende taak of kan extra code (vervolgcode) uitvoeren wanneer de asynchrone bewerking is voltooid. De maker van de vervolgcode heeft controle over waar die code wordt uitgevoerd. U kunt de vervolgcode expliciet maken, via methoden in de Task klasse (bijvoorbeeld ContinueWith) of impliciet, door taalondersteuning te gebruiken die is gebouwd op basis van voortzettingen (bijvoorbeeld await
in C#, Await
in Visual Basic, AwaitValue
in F#).
Taakstatus
De Task klasse biedt een levenscyclus voor asynchrone bewerkingen en die cyclus wordt vertegenwoordigd door de TaskStatus opsomming. Ter ondersteuning van randgevallen van typen die zijn afgeleid van Task en Task<TResult>, en om de scheiding van constructie van planning te ondersteunen, stelt de Task klasse een Start methode beschikbaar. Taken die door de publieke Task constructors worden gemaakt, worden koude taken genoemd, omdat ze hun levensloop in de onbekende Created toestand beginnen en pas worden gepland wanneer Start op deze exemplaren wordt aangeroepen.
Alle andere taken beginnen hun levenscyclus in een dynamische status, wat betekent dat de asynchrone bewerkingen die ze vertegenwoordigen al zijn gestart en hun taakstatus een andere opsommingswaarde is dan TaskStatus.Created. Alle taken die worden geretourneerd vanuit TAP-methoden, moeten worden geactiveerd. Als een TAP-methode intern gebruikmaakt van de constructor van een taak om de taak te instantiëren die moet worden geretourneerd, moet de TAP-methode het Start object aanroepen Task voordat het wordt geretourneerd. Consumenten van een TAP-methode kunnen er veilig van uitgaan dat de geretourneerde taak actief is en moeten niet proberen Start aan te roepen op een Task die wordt geretourneerd vanuit een TAP-methode. Het aanroepen van een actieve taak Start resulteert in een InvalidOperationException uitzondering.
Annulering (optioneel)
In TAP is annulering optioneel voor zowel asynchrone methode-implementers als asynchrone methodegebruikers. Als een bewerking annulering toestaat, wordt er een overbelasting weergegeven van de asynchrone methode die een annuleringstoken (CancellationToken instantie) accepteert. Volgens conventie heeft de parameter de naam cancellationToken
.
public Task ReadAsync(byte [] buffer, int offset, int count,
CancellationToken cancellationToken)
Public Function ReadAsync(buffer() As Byte, offset As Integer,
count As Integer,
cancellationToken As CancellationToken) _
As Task
De asynchrone bewerking bewaakt dit token op annuleringsaanvragen. Als het een annuleringsaanvraag ontvangt, kan het ervoor kiezen deze aanvraag te respecteren en de bewerking te annuleren. Als de annuleringsaanvraag resulteert in een voortijdige beëindiging van het werk, retourneert de TAP-methode een taak die eindigt op de Canceled status. Er is geen beschikbaar resultaat en er wordt geen uitzondering gegenereerd. De Canceled status wordt beschouwd als een definitieve (voltooide) status voor een taak, samen met de Faulted statussen.RanToCompletion Als een taak zich in de Canceled-status bevindt, waardoor de IsCompleted-eigenschap true
retourneert. Wanneer een taak in de Canceled status is voltooid, worden eventuele vervolgtaken die zijn geregistreerd bij de taak gepland of uitgevoerd, tenzij een vervolgoptie zoals NotOnCanceled is opgegeven om de vervolgactie over te slaan. Alle code die asynchroon wacht op een geannuleerde taak via het gebruik van taalfuncties, blijft actief, maar ontvangt een OperationCanceledException of een uitzondering die daaruit is afgeleid. Code die synchroon wordt geblokkeerd terwijl op de taak wordt gewacht via methoden zoals Wait en WaitAll die ook worden uitgevoerd met een uitzondering.
Als een annuleringstoken annulering heeft aangevraagd voordat de TAP-methode die dat token accepteert, wordt aangeroepen, moet de TAP-methode een Canceled taak retourneren. Als er echter annulering wordt aangevraagd terwijl de asynchrone bewerking wordt uitgevoerd, hoeft de asynchrone bewerking de annuleringsaanvraag niet te accepteren. De geretourneerde taak zou alleen in de Canceled-toestand moeten eindigen als de bewerking eindigt als gevolg van de annuleringsaanvraag. Als annulering wordt aangevraagd, maar er nog steeds een resultaat of een uitzondering wordt geproduceerd, moet de taak eindigen in de RanToCompletion of Faulted toestand.
Voor asynchrone methoden die in de eerste plaats de mogelijkheid willen bieden om geannuleerd te worden, hoeft u geen overload op te geven die geen annuleringstoken accepteert. Voor methoden die niet kunnen worden geannuleerd, biedt u geen overbelastingen die een annuleringstoken accepteren; dit helpt de aanroeper aan te geven of de doelmethode daadwerkelijk kan worden geannuleerd. Consumentencode die geen annulering wenst, kan een methode aanroepen die een CancellationToken methode accepteert en als argumentwaarde opgeeft None . None is functioneel gelijk aan de standaardwaarde CancellationToken.
Voortgangsrapportage (optioneel)
Sommige asynchrone bewerkingen profiteren van het bieden van voortgangsmeldingen; deze worden doorgaans gebruikt om een gebruikersinterface bij te werken met informatie over de voortgang van de asynchrone bewerking.
In TAP wordt de voortgang verwerkt via een IProgress<T> interface, die wordt doorgegeven aan de asynchrone methode als een parameter die meestal de naam progress
heeft. Door de voortgangsinterface op te geven wanneer de asynchrone methode wordt aangeroepen, kunnen racevoorwaarden worden geëlimineerd die het gevolg zijn van onjuist gebruik (dat wil gezegd, wanneer gebeurtenis-handlers die onjuist zijn geregistreerd nadat de bewerking is gestart, updates kunnen missen). Belangrijker is dat de voortgangsinterface verschillende implementaties van de voortgang ondersteunt, zoals wordt bepaald door de verbruikende code. De verbruikende code kan bijvoorbeeld geïnteresseerd zijn in alleen de meest recente statusupdate, of alle updates opslaan, of een actie voor elke update willen uitvoeren, of willen bepalen of de aanroep naar een specifieke thread wordt doorgevoerd. Al deze opties kunnen worden bereikt door een andere implementatie van de interface te gebruiken, aangepast aan de behoeften van de specifieke consument. Net als bij annulering moeten TAP-implementaties alleen een IProgress<T> parameter opgeven als de API voortgangsmeldingen ondersteunt.
Als de ReadAsync
methode die eerder in dit artikel is besproken, bijvoorbeeld tussenliggende voortgang kan rapporteren in de vorm van het aantal bytes dat tot nu toe is gelezen, kan de callback van de voortgang een IProgress<T> interface zijn:
public Task ReadAsync(byte[] buffer, int offset, int count,
IProgress<long> progress)
Public Function ReadAsync(buffer() As Byte, offset As Integer,
count As Integer,
progress As IProgress(Of Long)) As Task
Als een FindFilesAsync
methode een lijst retourneert van alle bestanden die voldoen aan een bepaald zoekpatroon, kan de callback van de voortgang een schatting geven van het percentage voltooid werk en de huidige set gedeeltelijke resultaten. Het kan deze informatie verstrekken met ofwel een tuple:
public Task<ReadOnlyCollection<FileInfo>> FindFilesAsync(
string pattern,
IProgress<Tuple<double,
ReadOnlyCollection<List<FileInfo>>>> progress)
Public Function FindFilesAsync(pattern As String,
progress As IProgress(Of Tuple(Of Double, ReadOnlyCollection(Of List(Of FileInfo))))) _
As Task(Of ReadOnlyCollection(Of FileInfo))
of met een gegevenstype dat specifiek is voor de API:
public Task<ReadOnlyCollection<FileInfo>> FindFilesAsync(
string pattern,
IProgress<FindFilesProgressInfo> progress)
Public Function FindFilesAsync(pattern As String,
progress As IProgress(Of FindFilesProgressInfo)) _
As Task(Of ReadOnlyCollection(Of FileInfo))
In het laatste geval wordt het speciale gegevenstype meestal voorzien ProgressInfo
van een achtervoegsel.
Als TAP-implementaties overloads hebben die een progress
-parameter accepteren, moeten ze toestaan dat de parameter null
is, en waarbij geen voortgang wordt gerapporteerd. TAP-implementaties moeten de voortgang van het Progress<T> object synchroon rapporteren, waardoor de asynchrone methode snel voortgang kan bieden. Daarnaast kan de consument van de voortgang bepalen hoe en waar de informatie het beste kan worden verwerkt. Het voortgangsexemplaar kan er bijvoorbeeld voor kiezen om callback-functies aan te sturen en evenementen op te roepen in een vastgelegde synchronisatiecontext.
IProgress<T>-implementaties
.NET biedt de Progress<T> klasse, die implementeert IProgress<T>. De Progress<T> klasse wordt als volgt gedeclareerd:
public class Progress<T> : IProgress<T>
{
public Progress();
public Progress(Action<T> handler);
protected virtual void OnReport(T value);
public event EventHandler<T>? ProgressChanged;
}
Een exemplaar van Progress<T> stelt een ProgressChanged gebeurtenis beschikbaar, die elke keer wordt geactiveerd wanneer de asynchrone bewerking een voortgangsupdate rapporteert. De ProgressChanged gebeurtenis wordt geactiveerd op het SynchronizationContext object dat is vastgelegd toen de instantie van Progress<T> werd geïnstantieerd. Als er geen synchronisatiecontext beschikbaar was, wordt een standaardcontext gebruikt die is gericht op de threadgroep. Handlers kunnen worden geregistreerd voor dit evenement. Een enkele handler kan ook voor het gemak aan de Progress<T> constructor worden verstrekt en gedraagt zich net als een gebeurtenis-handler voor de ProgressChanged gebeurtenis. Voortgangsupdates worden asynchroon doorgegeven om te voorkomen dat de asynchrone operatie wordt vertraagd terwijl eventhandlers worden uitgevoerd. Een andere IProgress<T> implementatie kan ervoor kiezen om verschillende semantiek toe te passen.
Kiezen van de overloads om te voorzien
Als een TAP-implementatie zowel de optionele als optionele CancellationTokenIProgress<T> parameters gebruikt, kan dit maximaal vier overbelastingen vereisen:
public Task MethodNameAsync(…);
public Task MethodNameAsync(…, CancellationToken cancellationToken);
public Task MethodNameAsync(…, IProgress<T> progress);
public Task MethodNameAsync(…,
CancellationToken cancellationToken, IProgress<T> progress);
Public MethodNameAsync(…) As Task
Public MethodNameAsync(…, cancellationToken As CancellationToken cancellationToken) As Task
Public MethodNameAsync(…, progress As IProgress(Of T)) As Task
Public MethodNameAsync(…, cancellationToken As CancellationToken,
progress As IProgress(Of T)) As Task
Veel TAP-implementaties bieden echter geen mogelijkheden voor annulering of voortgang, dus ze vereisen één methode:
public Task MethodNameAsync(…);
Public MethodNameAsync(…) As Task
Als een TAP-implementatie annulering of voortgang ondersteunt, maar niet beide, kan dit twee overbelastingen bieden:
public Task MethodNameAsync(…);
public Task MethodNameAsync(…, CancellationToken cancellationToken);
// … or …
public Task MethodNameAsync(…);
public Task MethodNameAsync(…, IProgress<T> progress);
Public MethodNameAsync(…) As Task
Public MethodNameAsync(…, cancellationToken As CancellationToken) As Task
' … or …
Public MethodNameAsync(…) As Task
Public MethodNameAsync(…, progress As IProgress(Of T)) As Task
Als een TAP-implementatie zowel annulering als voortgang ondersteunt, kan deze alle vier de overbelastingen blootstellen. Het kan echter slechts de volgende twee bieden:
public Task MethodNameAsync(…);
public Task MethodNameAsync(…,
CancellationToken cancellationToken, IProgress<T> progress);
Public MethodNameAsync(…) As Task
Public MethodNameAsync(…, cancellationToken As CancellationToken,
progress As IProgress(Of T)) As Task
Om te compenseren voor de twee ontbrekende tussenliggende combinaties, kunnen ontwikkelaars een standaardwaarde None doorgeven CancellationToken voor de cancellationToken
parameter en null
voor de progress
parameter.
Als u verwacht dat elk gebruik van de TAP-methode ondersteuning biedt voor annulering of voortgang, kunt u de overbelasting weglaten die de relevante parameter niet accepteren.
Als u besluit meerdere overloadfuncties beschikbaar te maken om annulering of voortgang optioneel te maken, moeten de overloads die geen ondersteuning bieden voor annulering of voortgang zich gedragen alsof er None voor annulering of null
voor voortgang aan de overload die deze wel ondersteunt is doorgegeven.
Verwante artikelen
Titel | Beschrijving |
---|---|
Asynchrone programmeerpatronen | Introduceert de drie patronen voor het uitvoeren van asynchrone bewerkingen: het op taken gebaseerde Asynchrone patroon (TAP), het Asynchrone programmeermodel (APM) en het op gebeurtenissen gebaseerde Asynchrone patroon (EAP). |
Het asynchrone patroon op basis van taken implementeren | Hierin wordt beschreven hoe u het op taken gebaseerde Asynchrone patroon (TAP) op drie manieren implementeert: met behulp van de C#- en Visual Basic-compilers in Visual Studio, handmatig of via een combinatie van de compiler en handmatige methoden. |
Het Taak-gebaseerde Asynchrone Patroon gebruiken | Hierin wordt beschreven hoe u taken en callbacks kunt gebruiken om te wachten zonder te blokkeren. |
Interoperabiliteit met andere asynchrone patronen en typen | Beschrijft hoe u het op taken gebaseerde Asynchrone patroon (TAP) gebruikt om het Asynchrone programmeermodel (APM) en op gebeurtenissen gebaseerde Asynchrone patroon (EAP) te implementeren. |