Sdílet prostřednictvím


Použití plánovačů

Plánovač řídí, kdy se odběr spustí a kdy se publikují oznámení. Skládá se ze tří komponent. Jedná se nejprve o datovou strukturu. Když plánujete dokončení úkolů, zařaďte je do plánovače, aby se na základě priority nebo jiných kritérií zařadily do fronty. Nabízí také kontext spuštění, který označuje, kde se úloha spouští (například ve fondu vláken, aktuálním vlákně nebo v jiné doméně aplikace). A konečně, má hodiny, které poskytují pojem času pro sebe (přístupem k Now vlastnosti plánovače). Úkoly naplánované v konkrétním plánovači budou dodržovat pouze čas označený danými hodinami.

Plánovače také zavádí pojem virtuálního času (označený typem VirtualScheduler), který neodpovídá reálnému času používanému v našem každodenním životě. Například dokončení sekvence, která je určená tak, aby trvalo 100 let, může být naplánována tak, aby se dokončila ve virtuálním čase za pouhých 5 minut. Tomu se budeme zabývat v tématu Testování a ladění pozorovatelných sekvencí .

Typy plánovače

Všechny různé typy plánovače poskytované Rx implementují rozhraní IScheduler . Každý z nich lze vytvořit a vrátit pomocí statických vlastností typu Plánovač. ImmediateScheduler (přístupem ke statické vlastnosti Immediate) spustí zadanou akci okamžitě. CurrentThreadScheduler (přístupem ke statické Vlastnosti CurrentThread) naplánuje akce, které mají být provedeny ve vlákně, které provádí původní volání. Akce se nespustí okamžitě, ale umístí se do fronty a provede se až po dokončení aktuální akce. DispatcherScheduler (pomocí přístupu ke statické vlastnosti Dispečera) naplánuje akce pro aktuálního dispečera, což je výhodné pro vývojáře silverlight, kteří používají Rx. Zadané akce jsou pak delegovány na metodu Dispatcher.BeginInvoke() v silverlightu. NewThreadScheduler (přístupem ke statické vlastnosti NewThread ) plánuje akce v novém vlákně a je optimální pro plánování dlouhotrvajících nebo blokujících akcí. TaskPoolScheduler (přístupem ke statické vlastnosti TaskPool ) plánuje akce pro konkrétní objekt pro vytváření úloh. ThreadPoolScheduler (přístupem k vlastnosti static ThreadPool ) plánuje akce ve fondu vláken. Oba plánovače fondů jsou optimalizované pro krátkodobé akce.

Použití plánovačů

Je možné, že jste již v kódu Rx použili plánovače, aniž byste explicitně uvedli typ plánovačů, které se mají použít. Je to proto, že všechny pozorovatelné operátory, které pracují se souběžností, mají více přetížení. Pokud jako argument nepoužíváte přetížení, které přijímá plánovač, vybere Rx výchozí plánovač pomocí principu nejnižší souběžnosti. To znamená, že je zvolen plánovač, který zavádí nejmenší míru souběžnosti, která vyhovuje potřebám operátoru.  Například pro operátory vracející pozorovatelný s omezeným a malým počtem zpráv volá Rx immediate.  Pro operátory vracející potenciálně velký nebo nekonečný počet zpráv je volána funkce CurrentThread . Pro operátory, které používají časovače, se používá ThreadPool .

Vzhledem k tomu, že Rx používá plánovač s nejnižší souběžností, můžete vybrat jiný plánovač, pokud chcete zavést souběžnost pro účely výkonu nebo pokud máte problém se spřažením vláken.  Příkladem prvního je, že pokud nechcete blokovat konkrétní vlákno, v tomto případě byste měli použít ThreadPool.  Příkladem toho druhého je, že pokud chcete, aby časovač běžel v uživatelském rozhraní, v tomto případě byste měli použít Dispatcher. Pokud chcete určit konkrétní plánovač, můžete použít přetížení operátoru, které přebírají plánovač, Timer(TimeSpan.FromSeconds(10), Scheduler.DispatcherScheduler())například .

V následujícím příkladu zdrojová pozorovatelná sekvence vytváří hodnoty zběsilým tempem. Výchozí přetížení operátoru Timer umístí zprávy OnNext na ThreadPool.

Observable.Timer(Timespan.FromSeconds(0.01))
          .Subscribe(…);

Tím se pozorovatel rychle zapíše do fronty. Tento kód můžeme vylepšit pomocí operátoru ObserveOn, který umožňuje určit kontext, který chcete použít k odesílání nabízených oznámení (OnNext) pozorovatelům. Ve výchozím nastavení operátor ObserveOn zajišťuje, že OnNext bude volána tolikrát, kolikrát je to možné v aktuálním vlákně. Můžete použít jeho přetížení a přesměrovat onNext výstupy do jiného kontextu. Kromě toho můžete pomocí operátoru SubscribeOn vrátit pozorovatelný proxy server, který deleguje akce na konkrétní plánovač. Například u aplikace náročné na uživatelské rozhraní můžete delegovat všechny operace na pozadí, které se mají provádět v plánovači spuštěném na pozadí, pomocí funkce SubscribeOn a předáním threadPoolScheduler. Chcete-li přijímat oznámení odesílaná a přistupovat k libovolnému prvku uživatelského rozhraní, můžete předat instanci DispatcherScheduler operátoru ObserveOn.

Následující příklad naplánuje všechna oznámení OnNext aktuálního dispečera tak, aby všechny hodnoty vysílané ven jsou odeslány ve vlákně uživatelského rozhraní. To je zvlášť užitečné pro vývojáře silverlightu, kteří používají Rx.

Observable.Timer(Timespan.FromSeconds(0.01))
          .ObserveOn(Scheduler.DispatcherScheduler)
          .Subscribe(…);

Místo použití operátoru ObserveOn ke změně kontextu spuštění, ve kterém pozorovatelná sekvence vytváří zprávy, můžeme vytvořit souběžnost na správném místě, abychom mohli začít. Vzhledem k tomu, že operátory parametrizují zavedení souběžnosti tím, že poskytují přetížení argumentu plánovače, předání správného plánovače povede k menšímu počtu míst, kde musí být použit operátor ObserveOn. Můžeme například odblokovat pozorovatele a přihlásit se k odběru vlákna uživatelského rozhraní přímo změnou plánovače používaného zdrojem, jako v následujícím příkladu. V tomto kódu budou všechny hodnoty vysílané z této pozorovatelné sekvence vycházet z vlákna uživatelského rozhraní pomocí přetížení časovače, který přijímá plánovač a poskytuje Scheduler.Dispatcher instanci.

Observable.Timer(Timespan.FromSeconds(0.01), Scheduler.DispatcherScheduler)
          .Subscribe(…);

Měli byste si také uvědomit, že pomocí operátoru ObservOn je naplánována akce pro každou zprávu, která prochází původní pozorovatelnou sekvencí. Tím se potenciálně změní informace o načasování a systém se tím ještě více zatěžuje. Pokud máte dotaz, který sestavuje různé pozorovatelné sekvence spuštěné v mnoha různých kontextech spuštění, a v dotazu provádíte filtrování, je nejlepší umístit ObservOn později do dotazu. Důvodem je to, že dotaz potenciálně odfiltruje velké množství zpráv a umístění operátoru ObserveOn dříve v dotazu by udělalo další práci se zprávami, které by se přesto odfiltrovaly. Volání operátoru ObserveOn na konci dotazu bude mít nejmenší dopad na výkon.

Další výhodou explicitního zadání typu plánovače je, že můžete zavést souběžnost pro účely výkonu, jak je znázorněno v následujícím kódu.

seq.GroupBy(...)
        .Select(x=>x.ObserveOn(Scheduler.NewThread))
        .Select(x=>expensive(x))  // perform operations that are expensive on resources