Anteckning
Åtkomst till den här sidan kräver auktorisering. Du kan prova att logga in eller ändra kataloger.
Åtkomst till den här sidan kräver auktorisering. Du kan prova att ändra kataloger.
I .NET är det aktivitetsbaserade asynkrona mönstret det rekommenderade asynkrona designmönstret för ny utveckling. Den baseras på typerna Task och Task<TResult> i System.Threading.Tasks namnområdet, som används för att representera asynkrona åtgärder.
Namngivning, parametrar och returtyper
TAP använder en enda metod för att representera initieringen och slutförandet av en asynkron åtgärd. Detta står i kontrast till både Asynkront programmeringsmönster (APM eller IAsyncResult) och det händelsebaserade asynkrona mönstret (EAP). APM kräver Begin och End metoder. EAP kräver en metod som har suffixet Async och som även kräver en eller flera händelser, händelsehanterardelegattyper och EventArg-härledda typer. Asynkrona metoder i TAP innehåller suffixet Async efter åtgärdsnamnet för metoder som returnerar väntande typer, till exempel Task, Task<TResult>, ValueTaskoch ValueTask<TResult>. En asynkron Get åtgärd som returnerar en Task<String> kan till exempel namnges GetAsync. Om du lägger till en TAP-metod i en klass som redan innehåller ett EAP-metodnamn med suffixet Async använder du suffixet TaskAsync i stället. Om klassen till exempel redan har en GetAsync metod använder du namnet GetTaskAsync. Om en metod startar en asynkron åtgärd men inte returnerar en väntande typ bör namnet börja med Begin, Starteller något annat verb som tyder på att den här metoden inte returnerar eller genererar resultatet av åtgärden.
En TAP-metod returnerar antingen en System.Threading.Tasks.Task eller en System.Threading.Tasks.Task<TResult>, baserat på om motsvarande synkrona metod returnerar void eller en typ TResult.
Parametrarna för en TAP-metod ska matcha parametrarna för dess synkrona motsvarighet och bör anges i samma ordning. Detta gäller dock inte parametrarna out och ref, och de bör undvikas helt. Alla data som har returnerats via en out- eller ref-parametern ska i stället returneras som en del av den TResult som returneras av Task<TResult>, och bör använda en tuppel eller en anpassad datastruktur för att tillgodose flera värden. Överväg också att lägga till en CancellationToken parameter även om TAP-metodens synkrona motsvarighet inte erbjuder någon.
Metoder som uteslutande ägnas åt att skapa, manipulera eller kombinera uppgifter (där metodens asynkrona avsikt är tydlig i metodnamnet eller i namnet på den typ som metoden tillhör) behöver inte följa det här namngivningsmönstret. sådana metoder kallas ofta för kombinatorer. Exempel på kombinatorer inkluderar WhenAll och WhenAny och beskrivs i avsnittet Använda de inbyggda aktivitetsbaserade kombinatorerna i artikeln Utnyttja det aktivitetsbaserade asynkrona mönstret.
Exempel på hur TAP-syntaxen skiljer sig från syntaxen som används i äldre asynkrona programmeringsmönster som Asynchronous Programming Model (APM) och det händelsebaserade Asynkrona mönstret (EAP) finns i Asynkrona programmeringsmönster.
Initiera en asynkron åtgärd
En asynkron metod som baseras på TAP kan utföra en liten mängd arbete synkront, till exempel verifiera argument och initiera den asynkrona åtgärden, innan den returnerar den resulterande aktiviteten. Synkront arbete bör hållas till ett minimum så att den asynkrona metoden kan återvända snabbt. Orsaker till en snabbretur är:
Asynkrona metoder kan anropas från användargränssnittstrådar (UI) och allt långvarigt synkront arbete kan skada programmets svarstider.
Flera asynkrona metoder kan startas samtidigt. Därför kan allt långvarigt arbete i den synkrona delen av en asynkron metod fördröja initieringen av andra asynkrona åtgärder, vilket minskar fördelarna med samtidighet.
I vissa fall är mängden arbete som krävs för att slutföra åtgärden mindre än mängden arbete som krävs för att starta åtgärden asynkront. Att läsa från en ström där läsåtgärden kan uppfyllas av data som redan är buffrade i minnet är ett exempel på ett sådant scenario. I sådana fall kan åtgärden slutföras synkront och returnera en uppgift som redan har slutförts.
Undantag
En asynkron metod bör generera ett undantag som bara genereras från det asynkrona metodanropet som svar på ett användningsfel. Användningsfel bör aldrig inträffa i produktionskoden. Om du till exempel skickar en null-referens (Nothing i Visual Basic) som ett av metodens argument orsakar ett feltillstånd (representeras vanligtvis av ett ArgumentNullException undantag), kan du ändra den anropande koden för att säkerställa att en null-referens aldrig skickas. För alla andra fel bör undantag som inträffar när en asynkron metod körs tilldelas till den returnerade aktiviteten, även om den asynkrona metoden utförs synkront innan aktiviteten returneras. Vanligtvis innehåller en aktivitet högst ett undantag. Men om aktiviteten representerar flera åtgärder (till exempel WhenAll), kan flera undantag associeras med en enda aktivitet.
Målmiljö
När du implementerar en TAP-metod kan du avgöra var asynkron körning sker. Du kan välja att köra arbetsbelastningen i trådpoolen, implementera den med hjälp av asynkron I/O (utan att vara bunden till en tråd under större delen av åtgärdens körning), köra den på en specifik tråd (till exempel användargränssnittstråden) eller använda valfritt antal potentiella kontexter. En TAP-metod kanske inte ens har något att köra och kan bara returnera en Task som representerar förekomsten av ett villkor någon annanstans i systemet (till exempel en uppgift som representerar data som anländer till en köad datastruktur).
Anroparen för TAP-metoden kan blockera väntan på att TAP-metoden ska slutföras genom att synkront vänta på den resulterande uppgiften, eller köra ytterligare kod (fortsättning) när den asynkrona åtgärden slutförs. Skaparen av fortsättningskoden har kontroll över var koden körs. Du kan skapa fortsättningskoden antingen explicit, via metoder i Task klassen (till exempel ContinueWith) eller implicit genom att använda språkstöd som bygger på fortsättningar (till exempel await i C#, Await i Visual Basic, AwaitValue i F#).
Uppgiftsstatus
Klassen Task tillhandahåller en livscykel för asynkrona åtgärder och den cykeln representeras av TaskStatus uppräkningen. För att stödja hörnfall av typer som härleds från Task och Task<TResult>, samt för att underlätta separationen av konstruktion och schemaläggning, exponerar klassen Task en Start metod. Uppgifter som skapas av de publika Task konstruktorerna kallas kalla uppgifter eftersom de börjar sin livscykel i ett icke-schemalagt Created läge och endast schemaläggs när Start anropas på dessa instanser.
Alla andra uppgifter börjar sin livscykel i ett hett tillstånd, vilket innebär att de asynkrona åtgärder som de representerar redan har initierats och deras aktivitetsstatus är ett annat uppräkningsvärde än TaskStatus.Created. Alla uppgifter som returneras från TAP-metoder måste aktiveras. Om en TAP-metod internt använder en uppgiftens konstruktor för att instansiera den uppgift som ska returneras, måste TAP-metoden anropa Start på Task-objektet före det returneras. Konsumenter av en TAP-metod kan tryggt anta att den returnerade uppgiften är aktiv och bör inte försöka anropa Start på någon Task som returneras från en TAP-metod. Att anropa Start på en aktiv uppgift resulterar i ett InvalidOperationException undantag.
Annullering (valfritt)
I TAP är annullering valfritt för både asynkrona metodverktyg och asynkrona metodanvändare. Om en operation tillåter annullering exponerar den en överbelastning av den asynkrona metoden som accepterar en annulleringstoken (CancellationToken instans). Enligt konventionen heter cancellationTokenparametern .
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
Den asynkrona åtgärden övervakar denna token för annulleringsbegäranden. Om den får en begäran om annullering kan den välja att uppfylla begäran och avbryta åtgärden. Om annulleringsbegäran resulterar i att arbetet avslutas i förtid returnerar TAP-metoden en aktivitet som slutar i Canceled tillståndet. Det finns inget tillgängligt resultat och inget undantag utlöses. Tillståndet Canceled anses vara ett slutligt (slutfört) tillstånd för en uppgift, tillsammans med tillstånden Faulted och RanToCompletion . Därför, om en aktivitet är i tillståndet Canceled, returnerar dess IsCompleted egenskap true. När en uppgift slutförs i tillståndet Canceled, schemaläggs eller körs eventuella fortsättningar som registrerats med uppgiften, såvida inte ett fortsättningsalternativ som NotOnCanceled angavs för att avregistrera sig från fortsättningen. All kod som asynkront väntar på en avbruten uppgift med hjälp av språkfunktioner fortsätter att köras men får ett OperationCanceledException eller ett undantag som härletts från den. Kod som synkront blockeras och väntar på uppgiften via metoder som Wait och WaitAll fortsätter även att köras i händelse av ett undantag.
Om en annulleringstoken har begärt annullering innan TAP-metoden som accepterar den token callas, bör TAP-metoden returnera en Canceled task. Men om annullering begärs medan den asynkrona åtgärden körs, behöver den asynkrona åtgärden inte acceptera annulleringsbegäran. Den returnerade aktiviteten bör endast sluta i Canceled tillståndet om åtgärden avslutas till följd av annulleringsbegäran. Om annullering begärs men ett resultat eller ett undantag fortfarande genereras bör uppgiften sluta i RanToCompletion eller Faulted tillståndet.
För asynkrona metoder som först och främst vill exponera möjligheten till avbrytning behöver du inte ange en överladdning som inte accepterar en avbrytstoken. För metoder som inte kan avbrytas ska du inte ange överlagringar som accepterar en annulleringstoken. Detta hjälper till att ange för anroparen om målmetoden faktiskt kan avbrytas. Konsumentkod som inte vill avbrytas kan anropa en metod som accepterar en CancellationToken och anger None som argumentvärde. None är funktionellt likvärdigt med standardvärdet CancellationToken.
Förloppsrapportering (valfritt)
Vissa asynkrona åtgärder har nytta av att tillhandahålla förloppsmeddelanden. Dessa används vanligtvis för att uppdatera ett användargränssnitt med information om förloppet för den asynkrona åtgärden.
I TAP hanteras förloppet via ett IProgress<T> gränssnitt, som skickas till den asynkrona metoden som en parameter som vanligtvis heter progress. Att tillhandahålla förloppsgränssnittet när den asynkrona metoden anropas hjälper till att eliminera konkurrensförhållanden som uppstår till följd av felaktig användning (dvs. när händelsehanterare som är felaktigt registrerade efter att åtgärden startar kan missa uppdateringar). Ännu viktigare är att förloppsgränssnittet stöder olika implementeringar av förloppet, vilket bestäms av den förbrukande koden. Den förbrukande koden kanske till exempel bara bryr sig om den senaste förloppsuppdateringen, eller vill buffra alla uppdateringar, eller vill anropa en åtgärd för varje uppdatering, eller vill styra om anropet är kopplat till en viss tråd. Alla dessa alternativ kan uppnås genom att använda en annan implementering av gränssnittet, anpassat efter den specifika konsumentens behov. Precis som vid annullering bör TAP-implementeringar endast tillhandahålla en IProgress<T> parameter om API:et stöder förloppsmeddelanden.
Om metoden ReadAsync som beskrivs tidigare i den här artikeln till exempel kan rapportera mellanliggande förlopp i form av antalet byte som lästs hittills kan återanropet för förlopp vara ett IProgress<T> gränssnitt:
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
Om en FindFilesAsync metod returnerar en lista över alla filer som uppfyller ett visst sökmönster kan förloppsåteranropet ge en uppskattning av procentandelen arbete som slutförts och den aktuella uppsättningen partiella resultat. Den kan ge den här informationen som en tupl:
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))
eller med en datatyp som är specifik för API:et:
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))
I det senare fallet får den särskilda datatypen vanligtvis suffixet ProgressInfo.
Om TAP-implementeringar ger överlagringar som accepterar en progress parameter måste de tillåta att argumentet är null, i vilket fall inga förlopp rapporteras. TAP-implementeringar bör rapportera förloppet till Progress<T> objektet synkront, vilket gör att den asynkrona metoden snabbt ger förlopp. Det gör också att konsumenten av förloppet kan avgöra hur och var bäst att hantera informationen. Till exempel kan förloppsinstansen välja att hantera återanrop och skapa händelser på en fångad synkroniseringskontext.
IProgress<T-implementeringar>
.NET tillhandahåller Progress<T> klassen som implementerar IProgress<T>. Klassen Progress<T> deklareras på följande sätt:
public class Progress<T> : IProgress<T>
{
public Progress();
public Progress(Action<T> handler);
protected virtual void OnReport(T value);
public event EventHandler<T>? ProgressChanged;
}
En instans av Progress<T> exponerar en ProgressChanged händelse som utlöses varje gång den asynkrona åtgärden rapporterar en förloppsuppdatering. Händelsen ProgressChanged aktiveras på objektet SynchronizationContext som registrerades när instansen Progress<T> instansierades. Om ingen synkroniseringskontext var tillgänglig används en standardkontext som riktar sig mot trådpoolen. Hanterare kan registreras för den här händelsen. En enskild hanterare kan också tillhandahållas konstruktorn för enkelhetens Progress<T> skull och fungerar precis som en händelsehanterare för ProgressChanged händelsen. Förloppsuppdateringar aktiveras asynkront för att undvika att fördröja den asynkrona åtgärden medan händelsehanterare körs. En annan IProgress<T> implementering kan välja att tillämpa olika semantik.
Välja de överbelastningar som ska tillhandahållas
Om en TAP-implementering använder både de valfria CancellationToken och valfria IProgress<T> parametrarna kan det kräva upp till fyra överlagringar:
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
Många TAP-implementeringar tillhandahåller dock inte funktioner för annullering eller förlopp, så de kräver en enda metod:
public Task MethodNameAsync(…);
Public MethodNameAsync(…) As Task
Om en TAP-implementering stödjer antingen annullering eller förlopp, men inte båda, kan den tillhandahålla två överbelastningar.
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
Om en TAP-implementering stöder både annullering och förlopp kan alla fyra överlagringarna exponeras. Det får dock endast innehålla följande två:
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
För att kompensera för de två saknade mellanliggande kombinationerna kan utvecklare skicka None eller ett standardvärde CancellationToken för parametern cancellationToken och null för parametern progress .
Om du förväntar dig att varje användning av TAP-metoden stöder annullering eller förlopp kan du utelämna de överlagringar som inte accepterar den relevanta parametern.
Om du bestämmer dig för att exponera flera överlagringar för att göra annullering eller förlopp valfria bör de överlagringar som inte stöder annullering eller förlopp fungera som om de skickades None för annullering eller null för förlopp till överbelastningen som stöder dessa.
Relaterade artiklar
| Titel | Beskrivning |
|---|---|
| Asynkrona programmeringsmönster | Introducerar de tre mönstren för att utföra asynkrona åtgärder: det aktivitetsbaserade Asynkrona mönstret (TAP), APM (Asynchronous Programming Model) och det händelsebaserade Asynkrona mönstret (EAP). |
| Implementera det aktivitetsbaserade asynkrona mönstret | Beskriver hur du implementerar det aktivitetsbaserade Asynkrona mönstret (TAP) på tre sätt: genom att använda C# och Visual Basic-kompilatorerna i Visual Studio manuellt eller genom en kombination av kompilatorn och manuella metoder. |
| Använda aktivitetsbaserat asynkront mönster | Beskriver hur du kan använda uppgiftshantering och callback-funktioner för att uppnå väntan utan att blockera. |
| Interop med andra asynkrona mönster och typer | Beskriver hur du använder det aktivitetsbaserat asynkront mönster (TAP) för att implementera asynkront programmodell (APM) och händelsebaserat asynkront mönster (EAP). |