Dela via


Metodtips för att implementera det händelsebaserade asynkrona mönstret

Det händelsebaserade asynkrona mönstret ger dig ett effektivt sätt att exponera asynkront beteende i klasser, med välbekanta händelser och delegera semantik. För att implementera händelsebaserat asynkront mönster måste du följa vissa specifika beteendekrav. I följande avsnitt beskrivs krav och riktlinjer som du bör tänka på när du implementerar en klass som följer det händelsebaserade asynkrona mönstret.

En översikt finns i Implementera det händelsebaserade asynkrona mönstret.

Nödvändiga beteendegarantier

Om du implementerar det händelsebaserade asynkrona mönstret måste du ange ett antal garantier för att din klass ska fungera korrekt och att klienter i klassen kan förlita sig på sådant beteende.

Slutförandet

Anropa alltid händelsehanteraren MethodNameCompleted när du har slutfört det, ett fel eller en annullering. Program bör aldrig stöta på en situation där de förblir inaktiva och slutförande aldrig inträffar. Ett undantag till den här regeln är om själva den asynkrona åtgärden är utformad så att den aldrig slutförs.

Slutförd händelse och EventArgs

Tillämpa följande designkrav för varje separat MethodNameAsync-metod :

  • Definiera en MethodNameCompleted-händelse i samma klass som metoden.

  • Definiera en EventArgs klass och tillhörande ombud för händelsen MethodNameCompleted som härleds från AsyncCompletedEventArgs klassen. Standardklassnamnet ska vara av formatet MethodNameCompletedEventArgs.

  • Kontrollera att EventArgs klassen är specifik för returvärdena för metoden MethodName . När du använder EventArgs klassen bör du aldrig kräva att utvecklare kastar resultatet.

    I följande kodexempel visas en bra och felaktig implementering av det här designkravet.

// Good design
private void Form1_MethodNameCompleted(object sender, xxxCompletedEventArgs e)
{
    DemoType result = e.Result;
}

// Bad design
private void Form1_MethodNameCompleted(object sender, MethodNameCompletedEventArgs e)
{
    DemoType result = (DemoType)(e.Result);
}
  • Definiera inte en EventArgs klass för att returnera metoder som returnerar void. Använd i stället en instans av AsyncCompletedEventArgs klassen.

  • Se till att du alltid genererar händelsen MethodNameCompleted . Den här händelsen bör aktiveras när den har slutförts, vid ett fel eller vid annullering. Program bör aldrig stöta på en situation där de förblir inaktiva och slutförande aldrig inträffar.

  • Se till att du fångar upp eventuella undantag som inträffar i den asynkrona åtgärden och tilldela det fångade undantaget till Error egenskapen.

  • Om det uppstod ett fel när uppgiften skulle slutföras bör resultatet inte vara tillgängligt. När egenskapen Error inte nullär kontrollerar du att åtkomsten till en egenskap i EventArgs strukturen genererar ett undantag. Använd metoden för att utföra den här verifieringen RaiseExceptionIfNecessary .

  • Modellera en timeout som ett fel. När en timeout inträffar genererar du händelsen MethodNameCompleted och tilldelar egenskapen en ErrorTimeoutException.

  • Om klassen stöder flera samtidiga anrop kontrollerar du att händelsen MethodNameCompleted innehåller rätt userSuppliedState objekt.

  • Se till att händelsen MethodNameCompleted aktiveras på rätt tråd och vid lämplig tidpunkt i programmets livscykel. Mer information finns i avsnittet Trådar och kontexter.

Köra åtgärder samtidigt

  • Om din klass har stöd för flera samtidiga anrop kan utvecklaren spåra varje anrop separat genom att definiera den MethodNameAsync-överlagring som tar en objektvärdestillståndsparameter, eller uppgifts-ID, med namnet userSuppliedState. Den här parametern bör alltid vara den sista parametern i metoden MethodNameAsyncs signatur.

  • Om klassen definierar överlagringen MethodNameAsync som tar en objektvärdestillståndsparameter, eller aktivitets-ID, bör du spåra åtgärdens livslängd med det aktivitets-ID:t och se till att ange den i slutförandehanteraren igen. Det finns hjälpklasser att hjälpa till med. Mer information om samtidighetshantering finns i Så här implementerar du en komponent som stöder det händelsebaserade asynkrona mönstret.

  • Om klassen definierar metoden MethodNameAsync utan tillståndsparametern och den inte stöder flera samtidiga anrop kontrollerar du att alla försök att anropa MethodNameAsync innan föregående MethodNameAsync-anrop har slutförts genererar en .InvalidOperationException

  • Skapa i allmänhet inget undantag om metoden MethodNameAsync utan parametern userSuppliedState anropas flera gånger så att det finns flera utestående åtgärder. Du kan skapa ett undantag när klassen uttryckligen inte kan hantera den situationen, men anta att utvecklare kan hantera dessa flera oskiljaktiga återanrop

Få åtkomst till resultat

Förloppsrapportering

  • Stöd för förloppsrapportering, om möjligt. Detta gör det möjligt för utvecklare att ge en bättre användarupplevelse för program när de använder din klass.

  • Om du implementerar en ProgressChanged- eller MethodNameProgressChanged-händelse kontrollerar du att inga sådana händelser har genererats för en viss asynkron åtgärd efter att åtgärdens MethodName Completed-händelse har aktiverats.

  • Om standarden ProgressChangedEventArgs fylls i kontrollerar du att ProgressPercentage den alltid kan tolkas som en procentandel. Procentandelen behöver inte vara korrekt, men den bör representera en procentandel. Om ditt mått för förloppsrapportering måste vara något annat än en procentandel härleder du en klass från ProgressChangedEventArgs klassen och lämnar ProgressPercentage vid 0. Undvik att använda ett annat rapporteringsmått än en procentandel.

  • Se till att händelsen ProgressChanged aktiveras på rätt tråd och vid lämplig tidpunkt i programmets livscykel. Mer information finns i avsnittet Trådar och kontexter.

IsBusy-implementering

  • Exponera inte en IsBusy egenskap om din klass stöder flera samtidiga anrop. XML-webbtjänstproxys exponerar till exempel inte en IsBusy egenskap eftersom de stöder flera samtidiga anrop av asynkrona metoder.

  • Egenskapen IsBusy bör returneras true efter att metoden MethodNameAsync har anropats och innan händelsen MethodNameCompleted har skapats. Annars bör den returnera false. Komponenterna BackgroundWorker och WebClient är exempel på klasser som exponerar en IsBusy egenskap.

Annullering

  • Supportavbokning, om möjligt. Detta gör det möjligt för utvecklare att ge en bättre användarupplevelse för program när de använder din klass.

  • Vid annullering anger du Cancelled flaggan i AsyncCompletedEventArgs objektet.

  • Se till att alla försök att komma åt resultatet genererar en InvalidOperationException uppgift om att åtgärden avbröts. Använd metoden för att utföra den här verifieringen AsyncCompletedEventArgs.RaiseExceptionIfNecessary .

  • Se till att anrop till en annulleringsmetod alltid returnerar korrekt och aldrig skapar ett undantag. I allmänhet meddelas inte en klient om huruvida en åtgärd verkligen kan avbrytas vid en viss tidpunkt och meddelas inte om en tidigare utfärdad annullering har lyckats. Programmet får dock alltid ett meddelande när en annullering lyckades, eftersom programmet deltar i slutförandestatusen.

  • Skapa händelsen MethodNameCompleted när åtgärden avbryts.

Fel och undantag

  • Fånga eventuella undantag som inträffar i den asynkrona åtgärden och ange värdet för AsyncCompletedEventArgs.Error egenskapen till det undantaget.

Trådning och kontexter

För att klassen ska fungera korrekt är det viktigt att klientens händelsehanterare anropas i rätt tråd eller kontext för den angivna programmodellen, inklusive ASP.NET- och Windows Forms-program. Två viktiga hjälpklasser tillhandahålls för att säkerställa att din asynkrona klass fungerar korrekt under alla programmodeller: AsyncOperation och AsyncOperationManager.

AsyncOperationManager tillhandahåller en metod, CreateOperation, som returnerar en AsyncOperation. Metoden MethodNameAsync anropar CreateOperation och klassen använder den returnerade AsyncOperation för att spåra livslängden för den asynkrona aktiviteten.

Om du vill rapportera förlopp, inkrementella resultat och slutförande till klienten anropar Post du metoderna och OperationCompletedAsyncOperation. AsyncOperation ansvarar för att ordna anrop till klientens händelsehanterare till rätt tråd eller kontext.

Kommentar

Du kan kringgå dessa regler om du uttryckligen vill gå emot principen för programmodellen, men ändå dra nytta av de andra fördelarna med att använda det händelsebaserade asynkrona mönstret. Du kanske till exempel vill att en klass som körs i Windows Forms ska vara fri trådad. Du kan skapa en kostnadsfri trådad klass så länge utvecklare förstår de underförstådda begränsningarna. Konsolprogram synkroniserar inte körningen av Post anrop. Detta kan leda ProgressChanged till att händelser höjs ur ordning. Om du vill ha serialiserad körning av Post anrop implementerar och installerar du en System.Threading.SynchronizationContext klass.

Mer information om hur du använder AsyncOperation och AsyncOperationManager för att aktivera asynkrona åtgärder finns i Så här implementerar du en komponent som stöder det händelsebaserade asynkrona mönstret.

Riktlinjer

  • Helst bör varje metodanrop vara oberoende av andra. Du bör undvika att koppla anrop med delade resurser. Om resurser ska delas mellan anrop måste du tillhandahålla en lämplig synkroniseringsmekanism i implementeringen.

  • Design som kräver att klienten implementerar synkronisering rekommenderas inte. Du kan till exempel ha en asynkron metod som tar emot ett globalt statiskt objekt som en parameter. flera samtidiga anrop av en sådan metod kan leda till skadade data eller dödlägen.

  • Om du implementerar en metod med överlagring av flera anrop (userState i signaturen) måste klassen hantera en samling användartillstånd eller uppgifts-ID:n och motsvarande väntande åtgärder. Den här samlingen bör skyddas med lock regioner eftersom de olika anropen lägger till och tar bort userState objekt i samlingen.

  • Överväg att återanvända CompletedEventArgs klasser där det är möjligt och lämpligt. I det här fallet är namngivningen inte konsekvent med metodnamnet, eftersom en viss delegat och EventArgs typ inte är knutna till en enda metod. Att tvinga utvecklare att omvandla värdet som hämtats från en egenskap på EventArgs är dock aldrig acceptabelt.

  • Om du redigerar en klass som härleds från Componentska du inte implementera och installera din egen SynchronizationContext klass. Programmodeller, inte komponenter, styr det SynchronizationContext som används.

  • När du använder multitrådning av något slag kan du utsätta dig för mycket allvarliga och komplexa buggar. Innan du implementerar en lösning som använder flertrådning kan du läsa Metodtips för hanterad trådning.

Se även