RxJavaスケジューラの使用例


253

RxJavaには、5つの異なるスケジューラーから選択できます。

  1. immediate():現在のスレッドですぐに作業を実行するスケジューラを作成して返します。

  2. trampoline():現在の作業が完了した後に実行される現在のスレッドで作業をキューに入れるスケジューラを作成して返します。

  3. newThread():作業単位ごとに新しいスレッドを作成するスケジューラを作成して返します。

  4. Calculation():計算作業を目的としたスケジューラを作成して返します。これは、イベントループ、コールバックの処理、およびその他の計算作業に使用できます。このスケジューラでIOバインド作業を実行しないでください。スケジューラを使用します。代わりにio()

  5. io():IOにバインドされた作業を目的としたスケジューラを作成して返します。実装は、必要に応じて拡張されるエグゼキュータスレッドプールによってサポートされます。これは、ブロッキングIOを非同期で実行するために使用できます。このスケジューラで計算作業を実行しないでください。スケジューラを使用します。代わりに計算()

質問:

最初の3つのスケジューラは自明です。ただし、計算ioについて少し混乱しています。

  1. 「IOバウンド作業」とは正確には何ですか?ストリーム(java.io)およびファイル(java.nio.files)の処理に使用されますか?データベースクエリに使用されますか?ファイルのダウンロードやREST APIへのアクセスに使用されますか?
  2. どのように計算は()とは異なる)newThread( ?それはすべての計算です()呼び出しが毎回新しい(バックグラウンド)スレッドではなく単一の(バックグラウンド)スレッド上にあるのですか?
  3. IO作業を行うときに計算()を呼び出すのはなぜ悪いのですか?
  4. 計算作業を行うときにio()を呼び出すのはなぜ悪いのですか?

回答:


332

すばらしい質問です。ドキュメントでもう少し詳しく説明できると思います。

  1. io()は無制限のスレッドプールに支えられており、CPUにあまり負荷をかけない、計算集約的でないタスクに使用するようなものです。したがって、ファイルシステムとのやり取り、別のホスト上のデータベースまたはサービスとのやり取りは良い例です。
  2. computation()利用可能なプロセッサの数に等しいサイズの制限されたスレッドプールによって支えられています。使用可能なプロセッサよりも多くのプロセッサでCPU集中型の作業を並行してスケジュールしようとした場合(たとえば、newThread())、、スレッドがプロセッサを争うため、スレッド作成オーバーヘッドとコンテキスト切り替えオーバーヘッドが発生し、パフォーマンスに大きな影響を与える可能性があります。
  3. computation()CPUを集中的に使用する作業だけに任せるのが最善です。そうしないと、CPU使用率が十分に得られません。
  4. io()2. io()で説明した理由により計算処理を呼び出すのは適切ではありません。1,000の計算タスクをio()並列でスケジュールすると、それらの1,000のタスクのそれぞれが独自のスレッドを持ち、CPUのコンテキストスイッチングコストで競合します。

5
RxJavaソースに精通している。これは長い間混乱の元でしたが、この点に関してドキュメントを強化する必要があると思います。
Dave Moten、2015

2
@IgorGanapolsky私はあなたがめったにしたくないことだと思います。すべての作業単位に対して新しいスレッドを作成しても、スレッドの構築と破棄に費用がかかるため、効率を上げることはめったにありません。通常は、computation()や他のスケジューラが行うスレッドを再利用したいとします。newThread()が正当な用途を持っている可能性があるのは(少なくとも私が考えることができる)唯一の、孤立した、まれで、長時間実行されるタスクの開始です。それでも、そのシナリオではio()を使用する可能性があります。
tmn 2015年

4
trampoline()が役立つ例を教えていただけますか?概念は理解していますが、実際に使用するシナリオを理解できません。それは私にはidがまだ謎ことだけスケジューラだ
TMN

32
ネットワーク呼び出しにはSchedulers.io()を使用し、同時ネットワーク呼び出しの数を制限する必要がある場合はScheduler.from(Executors.newFixedThreadPool(n))を使用します。
Dave Moten、2015年

4
timeoutデフォルトでcomputation()自分を置くとスレッドがブロックされると思うかもしれませんが、そうではありません。カバーの下でcomputation()使用するScheduledExecutorServiceので、時間遅延アクションはブロックされません。この事実を考えるとcomputation()、それが別のスレッドにある場合、スレッド切り替えコストが発生するため、良い考えです。
Dave Moten 2016年

3

最も重要な点は、Schedulers.ioSchedulers.computationの両方が、質問で述べた他のものとは対照的に、無制限のスレッドプールによってサポートされていることです。この特性は、ExecutornewCachedThreadPoolで作成された場合にのみ、Schedulers.from(Executor)によって共有されます。(自動再利用スレッドプールで無制限)でます。

以前の応答とWeb上の複数の記事で豊富に説明されているように、Schedulers.ioSchedulers.computationは、名前の作業の種類に合わせて最適化されているため、慎重に使用する必要があります。しかし、私の見解では、これらは最も重要な役割であり、リアクティブストリームに実際の同時実行性提供することです。

新規参入者の信念に反して、リアクティブストリームは本質的に並行ではなく、本質的に非同期でシーケンシャルです。このため、Schedulers.ioは、I / O操作がブロックされている場合にのみ使用されます(例:Apache IOUtils FileUtils.readFileAsString(...)などのブロックコマンドを使用)。)。これにより、操作がするまで呼び出しスレッドがフリーズします。完了しました。

Java AsynchronousFileChannel(...)などの非同期メソッドを使用しても、操作中に呼び出しスレッドがブロックされないため、別のスレッドを使用しても意味がありません。実際、Schedulers.ioスレッドは、イベントループを実行せず、コールバックが呼び出されないため、非同期操作にはあまり適していません。

同じロジックがデータベースアクセスまたはリモートAPI呼び出しに適用されます。Schedulers.ioを使用しないでください呼び出しに非同期またはリアクティブAPIを使用できる場合。

同時実行に戻ります。I / O操作を非同期的または同時に実行するための非同期またはリアクティブAPIにアクセスできない場合があるため、唯一の代替策は、個別のスレッドで複数の呼び出しをディスパッチすることです。悲しいかな、Reactiveストリームは両端でシーケンシャルですが、良いニュースはflatMap()オペレーターがコアで並行性を導入できることです。

並行性は、通常はflatMap()演算子を使用して、ストリーム構成で構築する必要があります。この強力なオペレーターは、flatMap()組み込みFunction <T、R>にマルチスレッドコンテキストを内部的に提供するように構成できます。そのコンテキストは、Scheduler.ioScheduler.computationなどのマルチスレッドスケジューラによって提供されます

RxJava2 スケジューラー並行性に関する記事で詳細を見つけてください。コードサンプルと、スケジューラーを連続して同時に使用する方法についての詳細な説明があります。

お役に立てれば、

Softjake


2

このブログ投稿は素晴らしい答えを提供します

ブログ投稿から:

Schedulers.io()は、無制限のスレッドプールによってサポートされています。これは、ファイルシステムとのやり取り、ネットワーク呼び出しの実行、データベースのやり取りなど、CPUを集中的に使用しないI / Oタイプの作業に使用されます。このスレッドプールは、非同期にブロッキングIOを実行するために使用することを目的としています。

Schedulers.computation()は、使用可能なプロセッサの数までのサイズの制限付きスレッドプールによってサポートされます。これは、画像のサイズ変更、大きなデータセットの処理など、計算またはCPUを集中的に使用する作業に使用されます。注意:使用可能なコアよりも多くの計算スレッドを割り当てると、スレッドが競合するため、コンテキストの切り替えとスレッド作成のオーバーヘッドにより、パフォーマンスが低下します。プロセッサの時間。

Schedulers.newThread()は、スケジュールされた作業単位ごとに新しいスレッドを作成します。このスケジューラは、新しいスレッドが毎回生成され、再利用が行われないため、負荷が高くなります。

Schedulers.from(Executor executor)は、指定されたエグゼキューターによってサポートされるカスタムスケジューラを作成して返します。スレッドプール内の同時スレッド数を制限するには、Scheduler.from(Executors.newFixedThreadPool(n))を使用します。これにより、すべてのスレッドが占有されているときにタスクがスケジュールされている場合、そのタスクはキューに入れられます。プール内のスレッドは、明示的にシャットダウンされるまで存在します。

メインスレッドまたはAndroidSchedulers.mainThread()は、RxAndroid拡張ライブラリによってRxJavaに提供されます。メインスレッド(UIスレッドとも呼ばれます)は、ユーザーインタラクションが発生する場所です。このスレッドをオーバーロードしないように注意して、不安定な応答のないUI、またはさらに悪いことに、アプリケーションが応答しない(ANR)ダイアログを防止する必要があります。

Schedulers.single()はRxJava 2の新機能です。このスケジューラは、要求された順序でタスクを順次実行する単一のスレッドによってサポートされています。

Schedulers.trampoline()は、参加しているワーカースレッドの1つによって、FIFO(先入れ先出し)方式でタスクを実行します。再帰を実装するときによく使用され、コールスタックの増大を回避します。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.