使用排程器
排程器可控制訂閱何時啟動,以及何時發佈通知。 它包含三個元件。 它是第一個資料結構。 當您排程工作完成時,會根據優先順序或其他準則,將它們放入排入佇列的排程器。 它也提供執行內容,表示工作執行的位置 (例如執行緒集區、目前線程或其他應用程式域中) 。 最後,它具有時鐘,可藉由存取 Now
排程器) 的 屬性,提供本身的時間概念 (。 在特定排程器上排程的工作只會遵守該時鐘所表示的時間。
排程器也會介紹 VirtualScheduler 類型) 所表示的虛擬時間 (概念,這不會與每日生活中使用的即時相互關聯。 例如,指定的序列需要 100 年才能完成,可以在 5 分鐘內排定在虛擬時間完成。 這將會涵蓋在 測試和偵錯可觀察序列 主題中。
排程器類型
Rx 所提供的各種排程器類型都會實作 IScheduler 介面。 您可以使用排程器類型的靜態屬性來建立和傳回這些屬性。 存取靜態的 ImmediateScheduler (,) 會立即啟動指定的動作。 透過存取靜態 CurrentThread 屬性來 (CurrentThreadScheduler) 將會排程在進行原始呼叫的執行緒上執行的動作。 動作不會立即執行,但會放在佇列中,而且只會在目前的動作完成之後執行。 DispatcherScheduler (透過存取靜態發送器屬性) 將會排程目前發送器的動作,這對使用 Rx 的 Silverlight 開發人員很有説明。 然後,指定的動作會委派給 Silverlight 中的 Dispatcher.BeginInvoke () 方法。 NewThreadScheduler (存取靜態 NewThread 屬性,) 排程新執行緒上的動作,最適合排程長時間執行或封鎖動作。 TaskPoolScheduler (,方法是存取靜態 TaskPool 屬性,) 排程特定 Task Factory 上的動作。 ThreadPoolScheduler (,方法是存取靜態 ThreadPool 屬性,) 排程執行緒集區上的動作。 這兩個集區排程器都已針對短期執行的動作進行優化。
使用排程器
您可能已在 Rx 程式碼中使用排程器,而不明確指出要使用的排程器類型。 這是因為處理並行的所有 Observable 運算子都有多個多載。 如果您未使用採用排程器做為引數的多載,Rx 會使用最小並行原則來挑選預設排程器。 這表示會選擇符合運算子需求的最少平行存取量排程器。 例如,對於傳回具有有限和少量訊息的可觀察運算子,Rx 會呼叫 Immediate。 對於傳回可能很大或無限數目訊息的運算子,會呼叫 CurrentThread 。 對於使用計時器的運算子,會使用 ThreadPool 。
因為 Rx 使用最少的並行排程器,所以如果您想要針對效能目的引進平行存取,或當您有線程親和性問題時,可以挑選不同的排程器。 先前的範例是當您不想封鎖特定執行緒時,在此情況下,您應該使用 ThreadPool。 後者的範例是當您想要在 UI 上執行計時器時,在此情況下,您應該使用 Dispatcher。 若要指定特定的排程器,您可以使用採用排程器的運算子多載,例如 Timer(TimeSpan.FromSeconds(10), Scheduler.DispatcherScheduler())
。
在下列範例中,來源可觀察序列會以隨機步調產生值。 Timer 運算子的預設多載會將 OnNext 訊息放在 ThreadPool 上。
Observable.Timer(Timespan.FromSeconds(0.01))
.Subscribe(…);
這會快速排入觀察者上的佇列。 我們可以使用 ObserveOn 運算子來改善此程式碼,這可讓您指定您想要用來傳送推播通知的內容, (OnNext) 觀察者。 根據預設,ObserveOn 運算子可確保 OnNext 會在目前線程上盡可能呼叫多次。 您可以使用其多載,並將 OnNext 輸出重新導向至不同的內容。 此外,您可以使用 SubscribeOn 運算子,將動作委派給特定排程器的 Proxy 可觀察。 例如,對於需要大量 UI 的應用程式,您可以使用 SubscribeOn 委派在背景中執行的所有背景作業,並將 ThreadPoolScheduler傳遞給它。 若要接收推出通知並存取任何 UI 元素,您可以將 DispatcherScheduler 的實例傳遞至 ObserveOn 運算子。
下列範例會排程目前發送器上的任何 OnNext 通知,以便在 UI 執行緒上傳送任何已推送的值。 這對使用 Rx 的 Silverlight 開發人員特別有説明。
Observable.Timer(Timespan.FromSeconds(0.01))
.ObserveOn(Scheduler.DispatcherScheduler)
.Subscribe(…);
我們可以在正確的位置建立平行存取,而不是使用 ObservOn 運算子來變更可觀察序列產生訊息的執行內容。 當運算子藉由提供排程器引數多載來參數化並行導入時,傳遞正確的排程器將會導致使用 ObserveOn 運算子的位置較少。 例如,我們可以藉由變更來源所使用的排程器,直接解除封鎖觀察者並訂閱 UI 執行緒,如下列範例所示。 在此程式碼中,使用採用排程器的 Timer 多載,並提供 Scheduler.Dispatcher
實例,從這個可觀察序列推送的所有值都會源自于 UI 執行緒。
Observable.Timer(Timespan.FromSeconds(0.01), Scheduler.DispatcherScheduler)
.Subscribe(…);
您也應該注意,藉由使用 ObservOn 運算子,會針對透過原始可觀察序列傳來的每個訊息排程動作。 這可能會變更計時資訊,以及對系統造成額外的壓力。 如果您有一個查詢,其組成在許多不同的執行內容上執行的各種可觀察序列,而且您在查詢中執行篩選,最好稍後在查詢中放置 ObservOn。 這是因為查詢可能會篩選掉許多訊息,而稍早在查詢中放置 ObserveOn 運算子會對會篩選掉的訊息執行額外工作。 在查詢結尾呼叫 ObserveOn 運算子將會產生最低效能影響。
明確指定排程器類型的另一個優點是您可以針對效能目的引進並行,如下列程式碼所示。
seq.GroupBy(...)
.Select(x=>x.ObserveOn(Scheduler.NewThread))
.Select(x=>expensive(x)) // perform operations that are expensive on resources