Architektura WPF – Dispatcher

2. díl - Architektura WPF – Dispatcher

Tomáš Jecha, MVP, MCSD       26.01.2012       WPF, Architektura, .NET       16241 zobrazení

Znát alespoň obrysy toho, jak funguje unvitř technologie, kterou využíváme je nezbytná nutnost. A právě nižší architektuře WPF se věnuje tento článek - konkrétně objektu Dispatcher.

Úvod

V předchozím díle jsem se věnoval WPF jen velmi obecně. Tento článek má za úkol představit objektovou architekturu této technologie, jejíž znalost je nezbytným základem pro pochopení všech hlubších principů.

Dispatcher

K čemu Dispatcher slouží?

Dispatcher je hnacím motorem celého WPF. Jedná se o komponentu, která je principiálně poměrně jednoduchá. Ve zkratce je to fronta úkolů zajišťující, že se úkoly vykonávají po sobě v rámci jednoho hlavního vlákna.

Obecně k uživatelskému prostředí v systému Windows zpravidla přistupujeme vždy z jednoho vlákna. Takové vlákno se nazývá “hlavní vlákno UI” (UI = user interface = uživatelského prostředí). A právě vše, co se v tomto hlavním vlákně bude vykonávat řídí onen Dispatcher. Takový přístup nazýváme single-treaded (jedno-vláknový). Je zde jak z historického hlediska, tak z hlediska čistě praktického.

Díky tomuto single-threaded modelu se nemusíme starat o celou řadu synchronizačních problémů. Pokud chceme, aby se v rámci uživatelského prostředí cokoliv stalo, přidáme úkol do Dispatcheru a on sám zajistí jeho spuštění v hlavním vlákně UI a ve správný čas. Máme tak jistotu, že se nebudou nikdy vykonávat dva úkoly týkající se uživatelského prostředí současně. Mezi tyto operace patří hlavně:

  • vykreslování
  • události uživatelského vstupu (kliknutí na tlačítko, stisk klávesy atp.)
  • změny vlastností (změna textu nadpisu okna, přidání položky do seznamu)
  • a obecně cokoliv, co předáme Dispatcheru do fronty událostí

Výhodou je, že o Dispatcheru nemusíte vůbec vědět, pokud ho nepotřebujete. Je to vnitřní princip WPF a vás v zásadě nemusí zajímat, že kliknutí na tlačítko znamená, že systém Windows pošle událost, která se přeloží na kliknutí a přidá do fronty Dispatcheru. Ten ji, hned jak to bude možné, vyvolá. Zjistí se, na který prvek uživatel kliknul a vyvolá jeho událost. My totiž pohodlně uvnitř této událost vykonáme potřebný kód a opět nás příliš nezajímá, že po jeho dokončení se Dispatcher opět ujme kontroly nad vláknem a čeká na další úkoly, popřípadě vykoná úkoly, které se mezitím nashromáždily.

“Vytuhnutí” single-threaded aplikací

První nevýhodu, na kterou narazíme (nebo spíš důsledek špatného použití) je stav, kdy některá operace v Dispatcheru bude trvat příliš dlouho dobu. V takovém případě celé uživatelské prostředí na dobu provádění operace lidově řečeno “vytuhne”. Dispatcher totiž bude s ostatními úkoly čekat na dokončení blokujícího úkolu. Například událost po stisknutí tlačítka na formuláři bude trvat 10 vteřin. A v tomto čase před dokončením neproběhne žádná událost reagující na vstup od uživatele, ani se žádným způsobem uživatelské prostředí nepřekreslí. Aplikace je tedy po tuto dobu “mrtvá”. Po dokončení operace se opět začnou provádět všechny čekající události a aplikace je opět ovladatelná. Pro uživatele je ale tento stav nepříjemný.

Základní princip aplikací, které reagují na vstupy od uživatelů i v průběhu těchto úkolů je vytvořit samostatné vlákno. Zde je ale důležité podotknout, že z toho samostatného vlákna nemůžeme ovládat uživatelské prostředí. Pokud to uděláme, příkaz skončí s chybou. Je to bezpečnostní mechanismus, který se má zabránit, aby do uživatelského prostředí zasáhlo jiné, než hlavní vlákno UI.

Pokud tedy chceme, aby výsledek operace (který ovlivní nějakým způsobem uživatelské prostředí) proběhl tak, jak je zamýšleno v single-threaded modelu, musíme tento úkol nechat provést právě hlavním vláknem UI. A to tak, že ji z pomocného vláka zaúkolovaného k vykonání dlouhé operace přidáme do úkolů Dispatcheru. Ten se o spuštění v hlavním vlákně postará.

Když tedy použití Dispatcheru shrnu, je důležité, aby operace, které se vykonávají v rámci hlavního vlákna netrvaly příliš dlouho. Po dobu trvání totiž aplikace nebude reagovat.

Životní cyklus Dispatcheru

Typicky je v každé WPF aplikaci jediná instance Dispatcheru. Ta se vytvoří při startu aplikace a stará se o běh hlavního vlákna. Dispatcher je zároveň také nositel informace o tom, zda se má aplikace ukončit. V tom případě opustí smyčku čekání na další úkoly a hlavní vlákno UI se ukončí a s tím i celá aplikace.

DispatcherObject

Téměř každý objekt ve WPF, se kterým chceme pracovat jen z hlavního UI vlákna dědí z třídy DispatcherObject. Ten slouží primárně ke dvou účelům:

  • Vlastnost dispatcherObject.Dispatcher - nese informaci, který Dispatcher se o tento objekt stará
  • Metoda dispatcherObject.CheckAccess – metoda vrací true, pokud ji voláme z vlákna o které se stará příslušný Dispatcher
  • Metoda a dispatcherObject.VerifyAccess - metoda, které vyvolá výjimku, pokud jsme v jiném vlákně, než v tom, o které se stará příslušný Dispatcher

Všechny grafické komponenty bez výjimky ve WPF dědí právě z třídy DispatcherObject. Zároveň je pravidlem, že většina jejich metod a vlastností volá metodu VerifyAccess. Tím se zajistí, že vznikne výjimka, pokud přistoupíme k metodám nebo vlastnostem těchto komponent z jiného, než hlavního vlákna UI.

Závěr

Doufám, že vás trochu kratší, teoretický úvod příliš neodradil. Věřím, že je to však důležitý základ, který pomůže objasnit fungování vašich WPF aplikací. V následujícím díle se vrátíme k návrhu uživatelského prostředí – jazyka XAML.

 

hodnocení článku

3 bodů / 3 hlasů       Hodnotit mohou jen registrované uživatelé.

 

Všechny díly tohoto seriálu

 

 

 

Nový příspěvek

 

Diskuse: Architektura WPF

Toto je poprvé (resp. podruhé s předchozím dílem) co čtu, že se někdo o uživatelském rozhraní vyjadřuje jako o uživatelském prostředí. Je to samozřejmě více méně jedno, ale ctím zažitou a obecně používanou terminologii.

Článek se mi líbí, jen škoda že nebyla uvedena jednoduchá ukázka použití Dispatcheru pro přidání úlohy do fronty (ne že bych na to nepřišel sám, kdybych to náhodou potřeboval). Něco jako ve Windows Forms:

WindowsFormsSynchronizationContext.Current.Post( _
New SendOrPostCallback(AddressOf DoWork), _
EventArgs.Empty)

Sub DoWork()
End Sub

nahlásit spamnahlásit spam -1 / 1 odpovědětodpovědět

Ve Windows Forms, WPF i jiné technologii je to zcela identické. Použije se System.Threading.SynchronizationContext.Current

Funkčně je to zcela identická vlastnost jako to, co jste uvedl, protože jako synchronizační kontext WPF naplní svůj DispatcherSynchronizationContext (poskytuje ho Dispatcher) a Windows Forms zase uvedený WindowsFormsSynchronizationContext. Takže je jedno, jestli jste ve WinForms nebo WPF. Například proto lze používat BackgroundWorker ve všech technologiích stejně.

Volání dispatcheru se ale budu věnovat v samostatném díle. Je to protože lze volit i priorita úloh, což může být v některých případech důležité a to bych rád probral.

A ano, terminologicky se používá více uživatelské rozhraní, to je určitě pravda. V tomhle případě ale nevěřím, že by to někoho mohlo zmást.

nahlásit spamnahlásit spam 1 / 3 odpovědětodpovědět
                       
Nadpis:
Antispam: Komu se občas házejí perly?
Příspěvek bude publikován pod identitou   anonym.

Nyní zakládáte pod článkem nové diskusní vlákno.
Pokud chcete reagovat na jiný příspěvek, klikněte na tlačítko "Odpovědět" u některého diskusního příspěvku.

Nyní odpovídáte na příspěvek pod článkem. Nebo chcete raději založit nové vlákno?

 

  • Administrátoři si vyhrazují právo komentáře upravovat či mazat bez udání důvodu.
    Mazány budou zejména komentáře obsahující vulgarity nebo porušující pravidla publikování.
  • Pokud nejste zaregistrováni, Vaše IP adresa bude zveřejněna. Pokud s tímto nesouhlasíte, příspěvek neodesílejte.

přihlásit pomocí externího účtu

přihlásit pomocí jména a hesla

Uživatel:  
Heslo:  

zapomenuté heslo

 

založit nový uživatelský účet

zaregistrujte se

 
zavřít

Nahlásit spam

Opravdu chcete tento příspěvek nahlásit pro porušování pravidel fóra?

Nahlásit Zrušit

Chyba

zavřít

feedback