Dela via


Implementera det händelsebaserade asynkrona mönstret

Om du skriver en klass med vissa åtgärder som kan medföra märkbara fördröjningar kan du överväga att ge den asynkrona funktioner genom att implementera det händelsebaserade asynkrona mönstret.

Det händelsebaserade asynkrona mönstret är ett standardiserat sätt att paketera en klass som har asynkrona funktioner. Om den implementeras med hjälpklasser som AsyncOperationManagerfungerar klassen korrekt under alla programmodeller, inklusive ASP.NET, konsolprogram och Windows Forms-program.

Ett exempel som implementerar det händelsebaserade asynkrona mönstret finns i Så här implementerar du en komponent som stöder det händelsebaserade asynkrona mönstret.

För enkla asynkrona åtgärder kan komponenten BackgroundWorker vara lämplig. Mer information om BackgroundWorkerfinns i Så här kör du en åtgärd i bakgrunden.

I följande lista beskrivs funktionerna i det händelsebaserade asynkrona mönster som beskrivs i det här avsnittet.

  • Möjligheter att implementera det händelsebaserade asynkrona mönstret

  • Namnge asynkrona metoder

  • Alternativt kan supporten avbrytas

  • Du kan också stödja egenskapen IsBusy

  • Alternativt kan du ge stöd för förloppsrapportering

  • Alternativt kan du ge stöd för att returnera inkrementella resultat

  • Hantera ut- och referensparametrar i metoder

Möjligheter att implementera det händelsebaserade asynkrona mönstret

Överväg att implementera det händelsebaserade asynkrona mönstret när:

  • Klienter i klassen behöver WaitHandle inte och IAsyncResult objekt som är tillgängliga för asynkrona åtgärder, vilket innebär att avsökning och WaitAll eller WaitAny måste byggas upp av klienten.

  • Du vill att asynkrona åtgärder ska hanteras av klienten med den välbekanta händelse-/delegatmodellen.

Alla åtgärder är en kandidat för en asynkron implementering, men de som du förväntar dig ska medföra långa svarstider bör övervägas. Särskilt lämpliga är åtgärder där klienter anropar en metod och meddelas när de har slutförts, utan ytterligare åtgärder krävs. Det är också lämpligt med åtgärder som körs kontinuerligt och regelbundet meddelar klienter om förlopp, inkrementella resultat eller tillståndsändringar.

Mer information om hur du bestämmer när du ska stödja det händelsebaserade asynkrona mönstret finns i Deciding When to Implement the Event-based Asynchronous Pattern (Bestämma när det händelsebaserade asynkrona mönstret ska implementeras).

Namnge asynkrona metoder

För varje synkron metod MethodName som du vill ange en asynkron motsvarighet för:

Definiera en MethodNameAsync-metod som:

  • Returnerar void.

  • Tar samma parametrar som metoden MethodName .

  • Accepterar flera anrop.

Du kan också definiera en MethodNameAsync-överlagring som är identisk med MethodNameAsync, men med ytterligare en objektvärdesparameter med namnet userState. Gör detta om du är beredd att hantera flera samtidiga anrop av din metod, i vilket fall userState värdet kommer att levereras tillbaka till alla händelsehanterare för att skilja anrop för metoden. Du kan också välja att göra detta helt enkelt som en plats för att lagra användartillstånd för senare hämtning.

För varje separat MethodNameAsync-metodsignatur :

  1. Definiera följande händelse i samma klass som metoden:

    Public Event MethodNameCompleted As MethodNameCompletedEventHandler
    
    public event MethodNameCompletedEventHandler MethodNameCompleted;
    
  2. Definiera följande ombud och AsyncCompletedEventArgs. Dessa kommer sannolikt att definieras utanför själva klassen, men i samma namnområde.

    Public Delegate Sub MethodNameCompletedEventHandler( _
        ByVal sender As Object, _
        ByVal e As MethodNameCompletedEventArgs)
    
    Public Class MethodNameCompletedEventArgs
        Inherits System.ComponentModel.AsyncCompletedEventArgs
    Public ReadOnly Property Result() As MyReturnType
    End Property
    
    public delegate void MethodNameCompletedEventHandler(object sender,
        MethodNameCompletedEventArgs e);
    
    public class MethodNameCompletedEventArgs : System.ComponentModel.AsyncCompletedEventArgs
    {
        public MyReturnType Result { get; }
    }
    
    • Kontrollera att klassen MethodNameCompletedEventArgs exponerar sina medlemmar som skrivskyddade egenskaper och inte fält, eftersom fält förhindrar databindning.

    • Definiera inte några AsyncCompletedEventArgs-härledda klasser för metoder som inte ger resultat. Använd bara en instans av AsyncCompletedEventArgs sig själv.

      Kommentar

      Det är helt acceptabelt, när det är möjligt och lämpligt, att återanvända ombud och AsyncCompletedEventArgs typer. I det här fallet är namngivningen inte lika konsekvent med metodnamnet, eftersom ett visst ombud och AsyncCompletedEventArgs inte är kopplat till en enda metod.

Alternativt kan supporten avbrytas

Om klassen stöder avbrutna asynkrona åtgärder bör annulleringen exponeras för klienten enligt beskrivningen nedan. Det finns två beslutspunkter som måste nås innan du definierar din avbokningssupport:

  • Har din klass, inklusive framtida förväntade tillägg till den, bara en asynkron åtgärd som stöder annullering?
  • Kan de asynkrona åtgärder som stöder annullering stödja flera väntande åtgärder? Det innebär att metoden MethodNameAsync tar en userState parameter och tillåter den flera anrop innan någon väntar på att slutföras?

Använd svaren på dessa två frågor i tabellen nedan för att avgöra vilken signatur för din avbokningsmetod som ska vara.

Visual Basic

Flera samtidiga åtgärder stöds Endast en åtgärd i taget
En Async-åtgärd i hela klassen Sub MethodNameAsyncCancel(ByVal userState As Object) Sub MethodNameAsyncCancel()
Flera Async-åtgärder i klassen Sub CancelAsync(ByVal userState As Object) Sub CancelAsync()

C#

Flera samtidiga åtgärder stöds Endast en åtgärd i taget
En Async-åtgärd i hela klassen void MethodNameAsyncCancel(object userState); void MethodNameAsyncCancel();
Flera Async-åtgärder i klassen void CancelAsync(object userState); void CancelAsync();

Om du definierar CancelAsync(object userState) metoden måste klienterna vara försiktiga när de väljer sina tillståndsvärden så att de kan skilja mellan alla asynkrona metoder som anropas på objektet, och inte bara mellan alla anrop av en enda asynkron metod.

Beslutet att namnge single-async-operation-versionen MethodNameAsyncCancel baseras på att enklare kunna identifiera metoden i en designmiljö som IntelliSense i Visual Studio. Detta grupperar de relaterade medlemmarna och skiljer dem från andra medlemmar som inte har något att göra med asynkrona funktioner. Om du förväntar dig att det kan läggas till ytterligare asynkrona åtgärder i efterföljande versioner är det bättre att definiera CancelAsync.

Definiera inte flera metoder från tabellen ovan i samma klass. Det kommer inte att vara meningsfullt, eller det kommer att röra klassgränssnittet med en spridning av metoder.

Dessa metoder returneras vanligtvis omedelbart, och åtgärden kan eller kanske inte avbryts. I händelsehanteraren för händelsen MethodNameCompleted innehåller objektet MethodNameCompletedEventArgs ett Cancelled fält som klienter kan använda för att avgöra om annulleringen inträffade.

Följ de annulleringssemantik som beskrivs i Metodtips för att implementera det händelsebaserade asynkrona mönstret.

Du kan också stödja egenskapen IsBusy

Om klassen inte stöder flera samtidiga anrop bör du överväga att exponera en IsBusy egenskap. Detta gör det möjligt för utvecklare att avgöra om en MethodNameAsync-metod körs utan att fånga ett undantag från metoden MethodNameAsync .

Följ semantiken IsBusy som beskrivs i Metodtips för att implementera det händelsebaserade asynkrona mönstret.

Alternativt kan du ge stöd för förloppsrapportering

Det är ofta önskvärt att en asynkron åtgärd rapporterar förloppet under åtgärden. Det händelsebaserade asynkrona mönstret innehåller en riktlinje för detta.

  • Du kan också definiera en händelse som ska aktiveras av den asynkrona åtgärden och anropas på lämplig tråd. Objektet ProgressChangedEventArgs har en förloppsindikator med heltalsvärde som förväntas vara mellan 0 och 100.

  • Namnge den här händelsen på följande sätt:

    • ProgressChanged om klassen har flera asynkrona åtgärder (eller förväntas växa till att omfatta flera asynkrona åtgärder i framtida versioner);

    • MethodNameProgressChanged om klassen har en enda asynkron åtgärd.

    Det här namngivningsvalet paralleller som gjorts för annulleringsmetoden, enligt beskrivningen i avsnittet Om du vill stödja annullering.

Den här händelsen bör använda ombudssignaturen ProgressChangedEventHandlerProgressChangedEventArgs och klassen. Om du kan ange en mer domänspecifik förloppsindikator (t.ex. byteläsning och totalt antal byte för en nedladdningsåtgärd) bör du definiera en härledd klass med ProgressChangedEventArgs.

Observera att det bara finns en ProgressChanged eller MethodNameProgressChanged-händelse för klassen, oavsett antalet asynkrona metoder som den stöder. Klienter förväntas använda objektet userState som skickas till metoderna MethodNameAsync för att skilja mellan förloppsuppdateringar för flera samtidiga åtgärder.

Det kan finnas situationer där flera åtgärder stöder förlopp och var och en returnerar en annan indikator för förloppet. I det här fallet är en enskild ProgressChanged händelse inte lämplig och du kan överväga att stödja flera ProgressChanged händelser. I det här fallet använder du ett namngivningsmönster för MethodNameProgressChanged för varje MethodNameAsync-metod.

Följ förloppsrapporteringssemantiken som beskrivs Metodtips för att implementera det händelsebaserade asynkrona mönstret.

Alternativt kan du ge stöd för att returnera inkrementella resultat

Ibland kan en asynkron åtgärd returnera inkrementella resultat innan den slutförs. Det finns ett antal alternativ som kan användas för att stödja det här scenariot. Några exempel följer.

Klass med en åtgärd

Om klassen bara stöder en enda asynkron åtgärd och åtgärden kan returnera inkrementella resultat:

  • ProgressChangedEventArgs Utöka typen för att bära inkrementella resultatdata och definiera en MethodNameProgressChanged-händelse med dessa utökade data.

  • Skapa den här Händelsen MethodNameProgressChanged när det finns ett inkrementellt resultat att rapportera.

Den här lösningen gäller specifikt för en klass med en enda asynkron åtgärd eftersom det inte finns några problem med att samma händelse inträffar för att returnera inkrementella resultat för "alla åtgärder", som händelsen MethodNameProgressChanged gör.

Klass med flera åtgärder med homogena inkrementella resultat

I det här fallet har klassen stöd för flera asynkrona metoder som var och en kan returnera inkrementella resultat, och dessa inkrementella resultat har alla samma typ av data.

Följ modellen som beskrivs ovan för klasser med en enda åtgärd eftersom samma EventArgs struktur fungerar för alla inkrementella resultat. Definiera en ProgressChanged händelse i stället för en MethodNameProgressChanged-händelse , eftersom den gäller för flera asynkrona metoder.

Klass med flera åtgärder med heterogena inkrementella resultat

Om klassen har stöd för flera asynkrona metoder, som var och en returnerar en annan typ av data, bör du:

  • Separera din inkrementella resultatrapportering från din förloppsrapportering.

  • Definiera en separat MethodNameProgressChanged-händelse med lämplig EventArgs för varje asynkron metod för att hantera metodens inkrementella resultatdata.

Anropa händelsehanteraren på lämplig tråd enligt beskrivningen i Metodtips för att implementera det händelsebaserade asynkrona mönstret.

Hantera ut- och referensparametrar i metoder

Även om användningen av out och ref i allmänhet inte rekommenderas i .NET, finns följande regler att följa när de finns:

Givet en synkron metod MethodName:

  • out parametrar till MethodName ska inte ingå i MethodNameAsync. I stället bör de vara en del av MethodNameCompletedEventArgs med samma namn som dess parametermotsvarighet i MethodName (om det inte finns ett lämpligare namn).

  • refparametrar till MethodName ska visas som en del av MethodNameAsync och som en del av MethodNameCompletedEventArgs med samma namn som dess parametermotsvarighet i MethodName (om det inte finns ett lämpligare namn).

Till exempel givet:

Public Function MethodName(ByVal arg1 As String, ByRef arg2 As String, ByRef arg3 As String) As Integer
public int MethodName(string arg1, ref string arg2, out string arg3);

Din asynkrona metod och dess AsyncCompletedEventArgs klass skulle se ut så här:

Public Sub MethodNameAsync(ByVal arg1 As String, ByVal arg2 As String)

Public Class MethodNameCompletedEventArgs
    Inherits System.ComponentModel.AsyncCompletedEventArgs
    Public ReadOnly Property Result() As Integer
    End Property
    Public ReadOnly Property Arg2() As String
    End Property
    Public ReadOnly Property Arg3() As String
    End Property
End Class
public void MethodNameAsync(string arg1, string arg2);

public class MethodNameCompletedEventArgs : System.ComponentModel.AsyncCompletedEventArgs
{
    public int Result { get; };
    public string Arg2 { get; };
    public string Arg3 { get; };
}

Se även