Přehled asynchronních vzorů založených na událostech

Aplikace, které provádějí mnoho úloh současně, ale zůstávají reagující na interakci uživatelů, často vyžadují návrh, který používá více vláken. Obor System.Threading názvů poskytuje všechny nástroje potřebné k vytváření vysoce výkonných multithreaded aplikací, ale použití těchto nástrojů efektivně vyžaduje značné zkušenosti s vícevláknovým softwarovým inženýrstvím. Pro relativně jednoduché vícevláknové aplikace BackgroundWorker poskytuje komponenta jednoduché řešení. U sofistikovanějších asynchronních aplikací zvažte implementaci třídy, která dodržuje asynchronní vzor založený na událostech.

Asynchronní vzor založený na událostech zpřístupňuje výhody vícevláknových aplikací a skrývá mnoho složitých problémů, které jsou součástí vícevláknového návrhu. Použití třídy, která podporuje tento vzor, vám umožní:

  • Provádění časově náročných úloh, jako jsou operace stahování a databáze na pozadí, bez přerušení aplikace.

  • Provádění více operací současně, přijímání oznámení po dokončení.

  • Počkejte, až budou prostředky dostupné bez zastavení ("blokování") vaší aplikace.

  • Komunikujte s čekajícími asynchronními operacemi pomocí známého modelu událostí a delegátů. Další informace o používání obslužných rutin událostí a delegátů naleznete v tématu Události.

Třída, která podporuje asynchronní vzor založený na událostech, bude mít jednu nebo více metod s názvem MethodNameAsync. Tyto metody mohou zrcadlit synchronní verze, které provádějí stejnou operaci v aktuálním vlákně. Třída může mít také MethodNameCompleted událost a může mít MethodNameAsyncCancel (nebo jednoduše CancelAsync) metoda.

PictureBox je typická komponenta, která podporuje asynchronní vzor založený na událostech. Image můžete stáhnout synchronně voláním jeho Load metody, ale pokud je image velká nebo pokud je síťové připojení pomalé, aplikace přestane reagovat, dokud se operace stahování nedokončí a volání, které se Load vrátí.

Pokud chcete, aby vaše aplikace běžela při načítání image, můžete metodu LoadAsync volat a zpracovat LoadCompleted událost stejně jako jakoukoli jinou událost. Když metodu LoadAsync zavoláte, bude vaše aplikace dál běžet, zatímco stahování pokračuje na samostatném vlákně ("na pozadí"). Obslužná rutina události bude volána po dokončení operace načítání obrázku a obslužná rutina události může prozkoumat AsyncCompletedEventArgs parametr a zjistit, jestli se stahování úspěšně dokončilo.

Asynchronní vzor založený na událostech vyžaduje, aby bylo možné zrušit asynchronní operaci a PictureBox ovládací prvek podporuje tento požadavek s jeho CancelAsync metodou. Volání CancelAsync odešle žádost o zastavení čekajícího stahování a při zrušení úkolu dojde k LoadCompleted vyvolání události.

Upozornění

Je možné, že stahování se dokončí stejně jako požadavek CancelAsync , takže Cancelled nemusí odrážet požadavek na zrušení. Tomu se říká časová podmínka a jedná se o běžný problém s vícevláknovým programováním. Další informace o problémech s vícevláknovým programováním najdete v tématu Osvědčené postupy pro spravované vlákno.

Charakteristiky asynchronního vzoru založeného na událostech

Asynchronní vzor založený na událostech může mít několik forem v závislosti na složitosti operací podporovaných konkrétní třídou. Nejjednodušší třídy mohou mít jednu metodu MethodNameAsync a odpovídající MethodNameCompleted událost. Složitější třídy mohou mít několik metod MethodNameAsync , z nichž každá má odpovídající MethodNameCompleted událost, stejně jako synchronní verze těchto metod. Třídy mohou volitelně podporovat zrušení, vykazování průběhu a přírůstkové výsledky pro každou asynchronní metodu.

Asynchronní metoda může také podporovat více čekajících volání (více souběžných volání), což kódu umožňuje volat ho libovolný početkrát předtím, než dokončí další čekající operace. Správné zpracování této situace může vyžadovat, aby vaše aplikace sledovala dokončení každé operace.

Příklady asynchronního vzoru založeného na událostech

PictureBox Komponenty SoundPlayer představují jednoduché implementace asynchronního vzoru založeného na událostech. BackgroundWorker Komponenty WebClient představují složitější implementace asynchronního vzoru založeného na událostech.

Níže je příklad deklarace třídy, která odpovídá vzoru:

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.  
}  

Fiktivní AsyncExample třída má dvě metody, z nichž obě podporují synchronní i asynchronní vyvolání. Synchronní přetížení se chovají jako jakékoli volání metody a provádějí operaci ve volajícím vlákně; pokud je operace časově náročná, může dojít ke znatelnému zpoždění před vrácením volání. Asynchronní přetížení spustí operaci v jiném vlákně a pak se okamžitě vrátí a umožní volajícímu vláknu pokračovat, zatímco operace se spustí na pozadí.

Asynchronní přetížení metody

Asynchronní operace mohou mít dvě přetížení: jednovolání a vícenásobné vyvolání. Tyto dvě formuláře můžete rozlišit pomocí jejich podpisů metody: formulář vícenásobného vyvolání má další parametr, který se nazývá userState. Tento formulář umožňuje, aby kód volal Method1Async(string param, object userState) vícekrát, aniž by čekal na dokončení čekajících asynchronních operací. Pokud se na druhou stranu pokusíte zavolat Method1Async(string param) před dokončením předchozího vyvolání, metoda vyvolá InvalidOperationException.

Parametr userState přetížení vícenásobného vyvolání umožňuje rozlišovat mezi asynchronními operacemi. Pro každé volání Method1Async(string param, object userState)zadáte jedinečnou hodnotu (například identifikátor GUID nebo hashový kód) a po dokončení každé operace může obslužná rutina události určit, která instance operace vyvolala událost dokončení.

Sledování čekajících operací

Pokud použijete přetížení vícenásobného vyvolání, bude váš kód muset sledovat userState objekty (ID úkolů) pro čekající úkoly. Pro každé volání Method1Async(string param, object userState)obvykle vygenerujete nový, jedinečný userState objekt a přidáte ho do kolekce. Když úloha odpovídající tomuto userState objektu vyvolá událost dokončení, implementace metody dokončení zkontroluje AsyncCompletedEventArgs.UserState a odebere ji z vaší kolekce. Tento způsob userState používá parametr roli ID úlohy.

Poznámka:

Při volání přetížení multiple-invocation musíte dávat pozor, abyste zadali jedinečnou hodnotu userState . Id úloh, která nejsou jedinečná, způsobí, že asynchronní třída vyvolá výjimku ArgumentException.

Zrušení čekajících operací

Před dokončením je důležité, abyste mohli kdykoli zrušit asynchronní operace. Třídy, které implementují asynchronní vzor založený na událostech, budou mít metodu CancelAsync (pokud existuje pouze jedna asynchronní metoda) nebo MethodNameAsyncCancel metoda (pokud existuje více asynchronních metod).

Metody, které umožňují více vyvolání, přebírají userState parametr, který lze použít ke sledování životnosti jednotlivých úloh. CancelAsyncuserState přebírá parametr, který umožňuje zrušit konkrétní čekající úkoly.

Metody, které podporují pouze jednu čekající operaci najednou, například Method1Async(string param), nejsou možné zrušit.

Příjem Aktualizace průběhu a přírůstkových výsledků

Třída, která dodržuje asynchronní vzor založený na událostech, může volitelně poskytnout událost pro sledování průběhu a přírůstkových výsledků. Obvykle se bude jmenovat ProgressChanged Nebo MethodNameProgressChanged a jeho odpovídající obslužná rutina události převezme ProgressChangedEventArgs parametr.

Obslužná rutina události události ProgressChanged může prozkoumat ProgressChangedEventArgs.ProgressPercentage vlastnost a určit procento asynchronní úlohy bylo dokončeno. Tato vlastnost bude v rozsahu od 0 do 100 a lze ji použít k aktualizaci Value vlastnosti objektu ProgressBar. Pokud čeká více asynchronních operací, můžete pomocí ProgressChangedEventArgs.UserState vlastnosti rozlišit, která operace hlásí průběh.

Některé třídy můžou inkrementální výsledky hlásit, jakmile budou pokračovat asynchronní operace. Tyto výsledky budou uloženy ve třídě, která je odvozena a ProgressChangedEventArgs zobrazí se jako vlastnosti v odvozené třídě. K těmto výsledkům můžete přistupovat v obslužné rutině ProgressChanged události události, stejně jako byste přistupovali k ProgressPercentage vlastnosti. Pokud čeká více asynchronních operací, můžete pomocí UserState vlastnosti rozlišit, která operace hlásí přírůstkové výsledky.

Viz také