Osvědčené postupy pro implementaci asynchronního vzoru založeného na událostech

Asynchronní vzor založený na událostech poskytuje efektivní způsob zveřejnění asynchronního chování ve třídách se známými sémantikou událostí a delegování. Pokud chcete implementovat asynchronní vzor založený na událostech, musíte postupovat podle určitých požadavků na chování. Následující části popisují požadavky a pokyny, které byste měli zvážit při implementaci třídy, která se řídí asynchronním vzorem založeným na událostech.

Přehled najdete v tématu Implementace asynchronního vzoru založeného na událostech.

Požadované záruky chování

Pokud implementujete asynchronní vzor založený na událostech, musíte poskytnout řadu záruk, abyste zajistili, že se vaše třída bude chovat správně a klienti vaší třídy mohou spoléhat na takové chování.

Dokončení

Vždy vyvolá obslužnou rutinu události MethodNameCompleted , pokud máte úspěšné dokončení, chybu nebo zrušení. Aplikace by nikdy neměly narazit na situaci, kdy zůstanou nečinné a k dokončení nikdy nedojde. Jednou z výjimek tohoto pravidla je, pokud je samotná asynchronní operace navržena tak, aby se nikdy nedokončí.

Dokončená událost a eventArgs

Pro každou samostatnou metodu MethodNameAsync použijte následující požadavky na návrh:

  • Definujte událost MethodNameCompleted ve stejné třídě jako metoda.

  • Definujte třídu a doprovodného EventArgs delegáta pro událost MethodNameCompleted, která je odvozena od AsyncCompletedEventArgs třídy. Výchozí název třídy by měl být form MethodNameCompletedEventArgs.

  • Ujistěte se, že EventArgs je třída specifická pro vrácené hodnoty MethodName metody. Při použití EventArgs třídy byste nikdy neměli vyžadovat, aby vývojáři přetypovávat výsledek.

    Následující příklad kódu ukazuje dobrou a špatnou implementaci tohoto požadavku na návrh v uvedeném pořadí.

// 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);
}
  • Nedefinujte EventArgs třídu pro vrácení metod, které vracejí void. Místo toho použijte instanci AsyncCompletedEventArgs třídy.

  • Ujistěte se, že vždy vyvoláte událost MethodNameCompleted . Tato událost by měla být vyvolána při úspěšném dokončení, v chybě nebo při zrušení. Aplikace by nikdy neměly narazit na situaci, kdy zůstanou nečinné a k dokončení nikdy nedojde.

  • Zachyťte všechny výjimky, ke kterým dochází v asynchronní operaci, a přiřaďte zachycenou výjimku vlastnosti Error .

  • Pokud při dokončení úkolu došlo k chybě, výsledky by neměly být přístupné. Error Pokud vlastnost není null, ujistěte se, že přístup k jakékoli vlastnosti ve EventArgs struktuře vyvolá výjimku. K provedení tohoto ověření použijte metodu RaiseExceptionIfNecessary .

  • Modeluje časový limit jako chybu. Pokud dojde k vypršení časového limitu , vytvořte událost MethodNameCompleted a přiřaďte TimeoutException k Error vlastnosti.

  • Pokud vaše třída podporuje více souběžných vyvolání, ujistěte se, že methodNameCompleted událost obsahuje příslušný userSuppliedState objekt.

  • Ujistěte se, že je událost MethodNameCompleted vyvolána v příslušném vlákně a v příslušné době v životním cyklu aplikace. Další informace najdete v části Vlákna a Kontexty.

Souběžné provádění operací

  • Pokud vaše třída podporuje více souběžných vyvolání, povolte vývojáři sledovat každé vyvolání samostatně definováním methodNameAsync přetížení, které přebírá parametr stavu s hodnotou objektu nebo ID úlohy volaného userSuppliedState. Tento parametr by měl být vždy posledním parametrem v podpisu metody MethodNameAsync .

  • Pokud vaše třída definuje methodNameAsync přetížení, které přebírá parametr stavu s hodnotou objektu nebo ID úkolu, nezapomeňte sledovat životnost operace s tímto ID úkolu a nezapomeňte ji vrátit zpět do obslužné rutiny dokončení. K dispozici jsou pomocné třídy, které vám pomůžou. Další informace o správě souběžnosti najdete v tématu Postupy: Implementace komponenty, která podporuje asynchronní vzor založený na událostech.

  • Pokud vaše třída definuje MethodNameAsync metoda bez parametru stavu a nepodporuje více souběžných vyvolání, ujistěte se, že všechny pokusy o vyvolání MethodNameAsync před předchozím voláním MethodNameAsync vyvolá .InvalidOperationException

  • Obecně platí, že nevyvolávejte výjimku, pokud metoda MethodNameAsync bez parametru userSuppliedState je vyvolána vícekrát, takže existuje více nevyřízených operací. Výjimku můžete vyvolat, když vaše třída explicitně nemůže tuto situaci zpracovat, ale předpokládejme, že vývojáři mohou zpracovávat tyto více nedistinguishable zpětné volání.

Přístup k výsledkům

Generování zpráv o průběhu

  • Pokud je to možné, podporují vykazování průběhu. Vývojáři tak můžou poskytovat lepší uživatelské prostředí aplikací, když používají vaši třídu.

  • Pokud implementujete událost ProgressChanged nebo MethodNameProgressChanged , ujistěte se, že pro konkrétní asynchronní operaci nejsou vyvolány žádné takové události po vyvolání události MethodNameCompleted .

  • Pokud se vyplní standard ProgressChangedEventArgs , ujistěte se, že ProgressPercentage je možné je vždy interpretovat jako procento. Procento nemusí být přesné, ale mělo by představovat procento. Pokud metrika generování sestav průběhu musí být něco jiného než procento, odvozujte třídu z ProgressChangedEventArgs třídy a nechte ProgressPercentage ji na 0. Nepoužívejte jinou metriku vytváření sestav než procento.

  • Ujistěte se, že ProgressChanged je událost vyvolána v příslušném vlákně a v příslušném čase v životním cyklu aplikace. Další informace najdete v části Vlákna a Kontexty.

Implementace IsBusy

  • Nevystavujte IsBusy vlastnost, pokud vaše třída podporuje více souběžných vyvolání. Například proxy webové služby XML nezpřístupňují IsBusy vlastnost, protože podporují více souběžných vyvolání asynchronních metod.

  • Vlastnost IsBusy by se měla vrátit true po zavolání metody MethodNameAsync a před vyvolání události MethodNameCompleted. V opačném případě by se měl vrátit false. WebClient Součástí BackgroundWorker jsou příklady tříd, které zpřístupňují IsBusy vlastnost.

Zrušení

  • Pokud je to možné, podporu zrušení. Vývojáři tak můžou poskytovat lepší uživatelské prostředí aplikací, když používají vaši třídu.

  • V případě zrušení nastavte Cancelled příznak v objektu AsyncCompletedEventArgs .

  • Zajistěte, aby všechny pokusy o přístup k výsledku vyvolaly InvalidOperationException oznámení, že operace byla zrušena. K provedení tohoto ověření použijte metodu AsyncCompletedEventArgs.RaiseExceptionIfNecessary .

  • Ujistěte se, že volání metody zrušení vždy vrátí úspěšně a nikdy nevyvolají výjimku. Obecně platí, že klient není upozorněn na to, zda je operace v daném okamžiku skutečně zrušená, a není upozorněna na to, zda bylo dříve vydané zrušení úspěšné. Aplikace se však vždy zobrazí oznámení, když bylo zrušení úspěšné, protože aplikace se účastní stavu dokončení.

  • Vyvolání události MethodNameCompleted při zrušení operace.

Chyby a výjimky

  • Zachyťte všechny výjimky, ke kterým dochází v asynchronní operaci, a nastavte hodnotu AsyncCompletedEventArgs.Error vlastnosti na danou výjimku.

Vlákna a kontexty

Pro správnou operaci třídy je důležité, aby obslužné rutiny událostí klienta byly vyvolány ve správném vlákně nebo kontextu pro daný aplikační model, včetně ASP.NET a model Windows Forms aplikací. K zajištění správného chování asynchronní třídy v libovolném aplikačním modelu jsou k dispozici dvě důležité pomocné třídy: AsyncOperation a AsyncOperationManager.

AsyncOperationManagerposkytuje jednu metodu, CreateOperationkterá vrací .AsyncOperation Volání metody CreateOperation MethodNameAsync a vaše třída používá vrácenou AsyncOperation ke sledování životnosti asynchronní úlohy.

Chcete-li hlásit průběh, přírůstkové výsledky a dokončení klientovi, zavolejte Post metody OperationCompleted v objektu AsyncOperation. AsyncOperation zodpovídá za zařazování volání obslužných rutin událostí klienta do správného vlákna nebo kontextu.

Poznámka:

Tato pravidla můžete obejít, pokud chcete explicitně přejít proti zásadám aplikačního modelu, ale přesto můžete využít další výhody použití asynchronního vzoru založeného na událostech. Můžete například chtít, aby třída provozovaná ve model Windows Forms byla bez vláken. Můžete vytvořit bezplatnou třídu s vlákny, pokud vývojáři chápou implicitní omezení. Konzolové aplikace nesynchronizují provádění Post volání. To může způsobit ProgressChanged vyvolání událostí mimo pořadí. Pokud chcete mít serializované provádění Post volání, implementujte a nainstalujte System.Threading.SynchronizationContext třídu.

Další informace o použití AsyncOperation a AsyncOperationManager povolení asynchronních operací naleznete v tématu Postupy: Implementace komponenty, která podporuje asynchronní vzor založený na událostech.

Pokyny

  • V ideálním případě by každé vyvolání metody mělo být nezávislé na ostatních. Měli byste se vyhnout vyvolání párů se sdílenými prostředky. Pokud se prostředky mají sdílet mezi vyvoláním, budete muset ve své implementaci poskytnout správný synchronizační mechanismus.

  • Návrhy, které vyžadují, aby klient implementoval synchronizaci, se nedoporučuje. Můžete mít například asynchronní metodu, která přijímá globální statický objekt jako parametr; více souběžných vyvolání takové metody může vést k poškození dat nebo zablokování.

  • Pokud implementujete metodu s přetížením vícenásobného vyvolání (userState v podpisu), vaše třída bude muset spravovat kolekci stavů uživatelů nebo ID úkolů a jejich odpovídající čekající operace. Tato kolekce by měla být chráněna oblastmi lock , protože různé vyvolání přidávají a odebírat userState objekty v kolekci.

  • Zvažte opětovné použití CompletedEventArgs tříd, pokud je to možné a vhodné. V tomto případě není pojmenování konzistentní s názvem metody, protože daný delegát a EventArgs typ nejsou svázané s jedinou metodou. Vynucení vývojářů přetypování hodnoty načtené z vlastnosti na objektu EventArgs není nikdy přijatelné.

  • Pokud vytváříte třídu odvozenou z Component, neimplementujte a nainstalujte vlastní SynchronizationContext třídu. Modely aplikací, nikoli komponenty, řídí SynchronizationContext , které se používají.

  • Pokud používáte multithreading jakéhokoli druhu, můžete se potenciálně vystavit velmi vážným a složitým chybám. Před implementací jakéhokoli řešení, které používá vícevláknové zpracování, si přečtěte osvědčené postupy pro spravované vlákno.

Viz také