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
Om det uppstod ett fel under körningen av den asynkrona åtgärden bör resultatet inte vara tillgängligt. Se till att åtkomsten till alla egenskaper i när AsyncCompletedEventArgsError inte
null
genererar undantaget som refereras av Error. Klassen AsyncCompletedEventArgs tillhandahåller metoden för det här ändamålet RaiseExceptionIfNecessary .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 .
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 enIsBusy
egenskap eftersom de stöder flera samtidiga anrop av asynkrona metoder.Egenskapen
IsBusy
bör returnerastrue
efter att metoden MethodNameAsync har anropats och innan händelsen MethodNameCompleted har skapats. Annars bör den returnerafalse
. Komponenterna BackgroundWorker och WebClient är exempel på klasser som exponerar enIsBusy
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 OperationCompleted på AsyncOperation. 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 medlock
regioner eftersom de olika anropen lägger till och tar bortuserState
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
- AsyncOperation
- AsyncOperationManager
- AsyncCompletedEventArgs
- ProgressChangedEventArgs
- BackgroundWorker
- Implementera det händelsebaserade asynkrona mönstret
- Händelsebaserat asynkront mönster (EAP)
- Bestämma när händelsebaserat asynkront mönster ska implementeras
- Metodtips för att implementera det händelsebaserade asynkrona mönstret
- Anvisningar: Använda komponenter som stöder det händelsebaserade asynkrona mönstret
- Anvisningar: Implementera en komponent som stöder det händelsebaserade asynkrona mönstret