スケジューラの使用

スケジューラは、サブスクリプションが開始されるタイミングと通知が発行されるタイミングを制御します。 これは、3 つのコンポーネントで構成されます。 最初はデータ構造です。 タスクを完了するようにスケジュールすると、優先度またはその他の条件に基づいて、キューに登録するためのスケジューラに配置されます。 また、タスクが実行される場所 (スレッド プール、現在のスレッド、別のアプリ ドメインなど) を示す実行コンテキストも提供されます。 最後に、(スケジューラの プロパティにアクセスすることによって) 時間の概念を提供する Now クロックがあります。 特定のスケジューラでスケジュールされているタスクは、そのクロックのみで示される時間に従います。

スケジューラでは、仮想時間 (VirtualScheduler 型で示されます) の概念も導入されています。これは、日常生活で使用されるリアルタイムとは相関しません。 たとえば、完了までに 100 年かかると指定されたシーケンスは、仮想時間でわずか 5 分で完了するようにスケジュールできます。 これについては、 監視可能シーケンスのテストとデバッグに関するトピックで説明します

スケジューラの種類

Rx によって提供されるさまざまなスケジューラの種類はすべて、 IScheduler インターフェイスを実装します。 これらはそれぞれ、Scheduler 型の静的プロパティを使用して作成および返すことができます。 ImmediateScheduler (静的な Immediate プロパティにアクセスすることによって) は、指定されたアクションをすぐに開始します。 CurrentThreadScheduler (静的 CurrentThread プロパティにアクセスすることによって) は、元の呼び出しを行うスレッドで実行されるアクションをスケジュールします。 アクションはすぐには実行されませんが、キューに配置され、現在のアクションが完了した後にのみ実行されます。 DispatcherScheduler (静的 Dispatcher プロパティにアクセスすることによって) は、現在の Dispatcher に対するアクションをスケジュールします。これは、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 演算子を使用して、特定のスケジューラにアクションを委任するプロキシ監視可能なを返すことができます。 たとえば、UI を集中的に使用するアプリケーションの場合は、SubscribeOn を使用して ThreadPoolScheduler を渡すことで、バックグラウンドで実行されているスケジューラに対して実行されるすべてのバックグラウンド操作を委任できます。 プッシュされた通知を受信し、UI 要素にアクセスするには、 DispatcherScheduler のインスタンスを ObserveOn 演算子に渡します。

次の例では、現在の Dispatcher で 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 演算子を呼び出すと、パフォーマンスへの影響が最も少なくなります。

スケジューラの種類を明示的に指定するもう 1 つの利点は、次のコードに示すように、パフォーマンスのためにコンカレンシーを導入できることです。

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