調(diào)度程序控制訂閱何時開始以及何時發(fā)布通知。它由三個組件組成。它首先是一個數(shù)據(jù)結(jié)構(gòu)。當(dāng)計劃要完成的任務(wù)時,它們被放入調(diào)度器以基于優(yōu)先級或其他標(biāo)準(zhǔn)進行排隊。它還提供了一個執(zhí)行上下文,它表示在哪里執(zhí)行任務(wù)(例如,在線程池,當(dāng)前線程或另一個應(yīng)用程序域中)。最后,它有一個時鐘為自己提供時間的概念(通過訪問調(diào)度器的Now屬性)。在特定調(diào)度程序上調(diào)度的任務(wù)將遵守僅由該時鐘表示的時間。 調(diào)度器還引入了虛擬時間的概念(由VirtualScheduler類型表示),其與在我們的日常生活中使用的實際時間不相關(guān)。例如,指定要花費100年時間完成的序列可以安排在僅僅5分鐘內(nèi)在虛擬時間內(nèi)完成。這將在測試和調(diào)試可觀察序列主題中介紹。
Rx提供的各種調(diào)度器類型都實現(xiàn)了IScheduler接口。可以通過使用Scheduler類型的靜態(tài)屬性來創(chuàng)建和返回這些對象。 ImmediateScheduler(通過訪問靜態(tài)Immediate屬性)將立即開始指定的操作。 CurrentThreadScheduler(通過訪問靜態(tài)CurrentThread屬性)將調(diào)度在執(zhí)行原始調(diào)用的線程上執(zhí)行的操作。操作不會立即執(zhí)行,而是放在隊列中,并且只在當(dāng)前操作完成后執(zhí)行。 DispatcherScheduler(通過訪問靜態(tài)Dispatcher屬性)將調(diào)度對當(dāng)前調(diào)度程序的操作,這對使用Rx的Silverlight開發(fā)人員有利。然后將指定的操作委派給Silverlight中的Dispatcher.BeginInvoke()方法。 NewThreadScheduler(通過訪問靜態(tài)NewThread屬性)在新線程上調(diào)度操作,并且是調(diào)度長時間運行或阻塞操作的最佳選擇。 TaskPoolScheduler(通過訪問靜態(tài)TaskPool屬性)在特定任務(wù)工廠調(diào)度操作。 ThreadPoolScheduler(通過訪問靜態(tài)ThreadPool屬性)調(diào)度線程池上的操作。兩個池調(diào)度程序都針對短時運行操作進行了優(yōu)化。
您可能已經(jīng)在Rx代碼中使用了調(diào)度程序,而沒有明確說明要使用的調(diào)度程序的類型。這是因為所有處理并發(fā)的Observable操作符都有多個重載。如果不使用將調(diào)度程序作為參數(shù)的重載,Rx將使用最小并發(fā)性原則選擇一個默認(rèn)調(diào)度程序。這意味著選擇引入滿足運營商需求的最少并發(fā)性的調(diào)度器。例如,對于返回具有有限和少量消息的observable的運算符,Rx調(diào)用Immediate。對于返回潛在大或無限數(shù)量的消息的操作符,調(diào)用CurrentThread。對于使用定時器的操作符,使用ThreadPool。 因為Rx使用最小并發(fā)性調(diào)度程序,如果您想為性能目的引入并發(fā)性,或者遇到線程相關(guān)性問題,則可以選擇不同的調(diào)度程序。前者的一個例子是,當(dāng)你不想阻塞一個特定的線程,在這種情況下,你應(yīng)該使用ThreadPool。后者的一個示例是,當(dāng)您想要在UI上運行計時器時,在這種情況下,您應(yīng)該使用Dispatcher。要指定特定的調(diào)度器,可以使用那些接受調(diào)度器的運算符重載,例如Timer(TimeSpan.FromSeconds(10),Scheduler.DispatcherScheduler())。 在以下示例中,源可觀察序列以瘋狂的速度生成值。 Timer運算符的默認(rèn)重載將在ThreadPool上放置OnNext消息。
Observable.Timer(Timespan.FromSeconds(0.01)) .Subscribe(…);這將在觀察者上快速排隊。我們可以通過使用ObserveOn運算符來改進此代碼,這允許您指定要用于將推送通知(OnNext)發(fā)送給觀察者的上下文。默認(rèn)情況下,ObserveOn運算符確保OnNext將在當(dāng)前線程上調(diào)用盡可能多的次數(shù)。您可以使用其重載并將OnNext輸出重定向到不同的上下文。此外,您可以使用SubscribeOn運算符返回將操作委派給特定調(diào)度程序的代理observable。例如,對于UI密集型應(yīng)用程序,您可以委派所有后臺操作在后臺運行的調(diào)度程序上使用SubscribeOn并傳遞給它一個ThreadPoolScheduler。為了接收被推出并且訪問任何UI元素的通知,您可以將DispatcherScheduler的實例傳遞給ObserveOn運算符。 以下示例將在當(dāng)前調(diào)度程序上計劃任何OnNext通知,以便在UI線程上發(fā)送任何推出的值。這對使用Rx的Silverlight開發(fā)人員特別有利。
Observable.Timer(Timespan.FromSeconds(0.01)) .ObserveOn(Scheduler.DispatcherScheduler) .Subscribe(…);而不是使用ObserveOn運算符來更改observable序列生成消息的執(zhí)行上下文,我們可以在正確的位置創(chuàng)建并發(fā)開始。 當(dāng)運算符通過提供調(diào)度程序參數(shù)重載來參數(shù)化并發(fā)性引入時,傳遞合適的調(diào)度程序?qū)?dǎo)致使用ObserveOn運算符的位置減少。 例如,我們可以通過更改源使用的調(diào)度器來解除阻塞觀察者并直接訂閱UI線程,如下面的示例所示。 在這段代碼中,通過使用Timer重載,它需要一個調(diào)度器,并提供Scheduler.Dispatcher實例,從這個可觀察序列推出的所有值都將來自UI線程。
Observable.Timer(Timespan.FromSeconds(0.01), Scheduler.DispatcherScheduler) .Subscribe(…);您還應(yīng)該注意,通過使用ObserveOn運算符,將為通過原始可觀察序列的每個消息計劃一個操作。 這可能改變定時信息以及對系統(tǒng)施加額外的壓力。 如果你有一個查詢,組成在許多不同的執(zhí)行上下文運行的各種可觀察序列,并且在查詢中進行過濾,最好在查詢中稍后放置ObserveOn。 這是因為查詢可能會過濾掉大量消息,并且將ObserveOn運算符放在查詢中較早的位置會對將被過濾掉的消息執(zhí)行額外的工作。 在查詢結(jié)束時調(diào)用ObserveOn運算符將產(chǎn)生最小的性能影響。 明確指定調(diào)度程序類型的另一個優(yōu)點是,您可以為性能目的引入并發(fā)性,如以下代碼所示。
seq.GroupBy(...) .Select(x=>x.ObserveOn(Scheduler.NewThread)) .Select(x=>expensive(x)) // perform Operations that are expensive on resources新聞熱點
疑難解答