Dela via


Händelsebaserad översikt över asynkront mönster

Program som utför många uppgifter samtidigt, men ändå förblir dynamiska för användarinteraktion, kräver ofta en design som använder flera trådar. Namnområdet System.Threading innehåller alla verktyg som krävs för att skapa högpresterande flertrådade program, men att använda dessa verktyg kräver effektivt betydande erfarenhet av flertrådad programvaruteknik. För relativt enkla flertrådade program är komponenten BackgroundWorker en enkel lösning. För mer avancerade asynkrona program bör du överväga att implementera en klass som följer det händelsebaserade asynkrona mönstret.

Det händelsebaserade asynkrona mönstret gör fördelarna med flertrådade program tillgängliga samtidigt som många av de komplexa problem som ingår i flertrådad design döljs. Med hjälp av en klass som stöder det här mönstret kan du:

  • Utför tidskrävande uppgifter, till exempel nedladdningar och databasåtgärder, "i bakgrunden", utan att avbryta programmet.

  • Kör flera åtgärder samtidigt och ta emot meddelanden när var och en är klar.

  • Vänta tills resurserna blir tillgängliga utan att stoppa ("blockera") ditt program.

  • Kommunicera med väntande asynkrona åtgärder med hjälp av modellen för välbekanta händelser och ombud. Mer information om hur du använder händelsehanterare och ombud finns i Händelser.

En klass som stöder det händelsebaserade asynkrona mönstret har en eller flera metoder med namnet MethodNameAsync. Dessa metoder kan spegla synkrona versioner som utför samma åtgärd på den aktuella tråden. Klassen kan också ha en MethodNameCompleted-händelse och den kan ha en MethodNameAsyncCancel-metod (eller helt enkelt CancelAsync).

PictureBox är en typisk komponent som stöder det händelsebaserade asynkrona mönstret. Du kan ladda ned en bild synkront genom att anropa dess Load metod, men om avbildningen är stor, eller om nätverksanslutningen är långsam, slutar programmet att svara tills nedladdningsåtgärden har slutförts och anropet till Load returneras.

Om du vill att programmet ska fortsätta att köras medan avbildningen läses in kan du anropa LoadAsync metoden och hantera LoadCompleted händelsen, precis som du hanterar alla andra händelser. När du anropar LoadAsync metoden fortsätter programmet att köras medan nedladdningen fortsätter på en separat tråd ("i bakgrunden"). Händelsehanteraren anropas när avbildningsinläsningen är klar och händelsehanteraren kan undersöka parametern AsyncCompletedEventArgs för att avgöra om nedladdningen har slutförts.

Det händelsebaserade asynkrona mönstret kräver att en asynkron åtgärd kan avbrytas och PictureBox kontrollen stöder det här kravet med sin CancelAsync metod. Samtal CancelAsync skickar en begäran om att stoppa den väntande nedladdningen, och när uppgiften avbryts LoadCompleted utlöses händelsen.

Varning

Det är möjligt att nedladdningen slutförs precis som CancelAsync begäran görs, så Cancelled kanske inte återspeglar begäran om att avbryta. Detta kallas ett konkurrenstillstånd och är ett vanligt problem i flertrådad programmering. Mer information om problem med flertrådad programmering finns i Metodtips för hanterad trådning.

Egenskaper för det händelsebaserade asynkrona mönstret

Det händelsebaserade asynkrona mönstret kan ha flera former, beroende på komplexiteten i de åtgärder som stöds av en viss klass. De enklaste klasserna kan ha en enda MethodNameAsync-metod och en motsvarande MethodNameCompleted-händelse . Mer komplexa klasser kan ha flera MethodNameAsync-metoder , var och en med en motsvarande MethodNameCompleted-händelse , samt synkrona versioner av dessa metoder. Klasser kan också stödja annullering, förloppsrapportering och inkrementella resultat för varje asynkron metod.

En asynkron metod kan också ha stöd för flera väntande anrop (flera samtidiga anrop), vilket gör att koden kan anropa den ett antal gånger innan den slutför andra väntande åtgärder. Korrekt hantering av den här situationen kan kräva att ditt program spårar slutförandet av varje åtgärd.

Exempel på händelsebaserat asynkront mönster

Komponenterna SoundPlayer och PictureBox representerar enkla implementeringar av det händelsebaserade asynkrona mönstret. Komponenterna WebClient och BackgroundWorker representerar mer komplexa implementeringar av det händelsebaserade asynkrona mönstret.

Nedan visas en exempelklassdeklaration som överensstämmer med mönstret:

Public Class AsyncExample  
    ' Synchronous methods.  
    Public Function Method1(ByVal param As String) As Integer
    Public Sub Method2(ByVal param As Double)
  
    ' Asynchronous methods.  
    Overloads Public Sub Method1Async(ByVal param As String)
    Overloads Public Sub Method1Async(ByVal param As String, ByVal userState As Object)
    Public Event Method1Completed As Method1CompletedEventHandler  
  
    Overloads Public Sub Method2Async(ByVal param As Double)
    Overloads Public Sub Method2Async(ByVal param As Double, ByVal userState As Object)
    Public Event Method2Completed As Method2CompletedEventHandler  
  
    Public Sub CancelAsync(ByVal userState As Object)
  
    Public ReadOnly Property IsBusy () As Boolean  
  
    ' Class implementation not shown.  
End Class  
public class AsyncExample  
{  
    // Synchronous methods.  
    public int Method1(string param);  
    public void Method2(double param);  
  
    // Asynchronous methods.  
    public void Method1Async(string param);  
    public void Method1Async(string param, object userState);  
    public event Method1CompletedEventHandler Method1Completed;  
  
    public void Method2Async(double param);  
    public void Method2Async(double param, object userState);  
    public event Method2CompletedEventHandler Method2Completed;  
  
    public void CancelAsync(object userState);  
  
    public bool IsBusy { get; }  
  
    // Class implementation not shown.  
}  

Den fiktiva AsyncExample klassen har två metoder som båda stöder synkrona och asynkrona anrop. Synkrona överlagringar fungerar som alla metodanrop och kör åtgärden på den anropande tråden. Om åtgärden är tidskrävande kan det uppstå en märkbar fördröjning innan anropet returneras. De asynkrona överlagringarna startar åtgärden på en annan tråd och returnerar sedan omedelbart, vilket gör att den anropande tråden kan fortsätta medan åtgärden körs "i bakgrunden".

Asynkrona metodöverlagringar

Det finns potentiellt två överlagringar för de asynkrona åtgärderna: enkel anrop och flera anrop. Du kan särskilja dessa två formulär med deras metodsignaturer: formuläret för flera anrop har en extra parameter med namnet userState. Det här formuläret gör det möjligt för koden att anropa Method1Async(string param, object userState) flera gånger utan att vänta på att väntande asynkrona åtgärder ska slutföras. Om du däremot försöker anropa Method1Async(string param) innan ett tidigare anrop har slutförts genererar metoden en InvalidOperationException.

Med userState parametern för överlagring av flera anrop kan du skilja mellan asynkrona åtgärder. Du anger ett unikt värde (till exempel ett GUID eller hash-kod) för varje anrop till Method1Async(string param, object userState), och när varje åtgärd har slutförts kan händelsehanteraren avgöra vilken instans av åtgärden som genererade slutförandehändelsen.

Spåra väntande åtgärder

Om du använder överlagringar av flera anrop måste koden hålla reda på objekten userState (aktivitets-ID:erna) för väntande aktiviteter. För varje anrop till Method1Async(string param, object userState)genererar du vanligtvis ett nytt unikt userState objekt och lägger till det i en samling. När uppgiften som motsvarar det här userState objektet genererar slutförandehändelsen kommer implementeringen av slutförandemetoden att undersöka AsyncCompletedEventArgs.UserState och ta bort den från samlingen. På så sätt tar parametern userState rollen som ett aktivitets-ID.

Kommentar

Du måste vara noga med att ange ett unikt värde för userState i dina anrop till överlagring av flera anrop. Icke-unika aktivitets-ID:er gör att den asynkrona klassen genererar en ArgumentException.

Avbryter väntande åtgärder

Det är viktigt att kunna avbryta asynkrona åtgärder när som helst innan de slutförs. Klasser som implementerar det händelsebaserade asynkrona mönstret har en CancelAsync metod (om det bara finns en asynkron metod) eller en MethodNameAsyncCancel-metod (om det finns flera asynkrona metoder).

Metoder som tillåter flera anrop tar en userState parameter som kan användas för att spåra livslängden för varje uppgift. CancelAsync tar en userState parameter som gör att du kan avbryta vissa väntande aktiviteter.

Metoder som endast stöder en enda väntande åtgärd i taget, till exempel Method1Async(string param), kan inte avbrytas.

Ta emot förlopp Uppdateringar och inkrementella resultat

En klass som följer det händelsebaserade asynkrona mönstret kan eventuellt tillhandahålla en händelse för spårning av förlopp och inkrementella resultat. Detta namnges ProgressChanged vanligtvis eller MethodNameProgressChanged, och dess motsvarande händelsehanterare tar en ProgressChangedEventArgs parameter.

Händelsehanteraren för händelsen kan undersöka ProgressChangedEventArgs.ProgressPercentage egenskapen för ProgressChanged att avgöra vilken procentandel av en asynkron aktivitet som har slutförts. Den här egenskapen sträcker sig från 0 till 100 och kan användas för att uppdatera Value egenskapen för en ProgressBar. Om flera asynkrona åtgärder väntar kan du använda ProgressChangedEventArgs.UserState egenskapen för att särskilja vilken åtgärd som rapporterar förlopp.

Vissa klasser kan rapportera inkrementella resultat när asynkrona åtgärder fortsätter. Dessa resultat lagras i en klass som härleds från ProgressChangedEventArgs och de visas som egenskaper i den härledda klassen. Du kan komma åt dessa resultat i händelsehanteraren för ProgressChanged händelsen, precis som du skulle komma åt ProgressPercentage egenskapen. Om flera asynkrona åtgärder väntar kan du använda UserState egenskapen för att särskilja vilken åtgärd som rapporterar inkrementella resultat.

Se även