TPL Dataflow は、Rx の機能の特殊なサブセットをカバーしていると言えます。データフローは、かなりの時間がかかる可能性のあるデータ処理用です。一方、Rx は、処理時間が無視できるマウス位置、エラー状態などのイベント用です。
例: 「subscribe」ハンドラーが非同期で、一度に 1 つ以上のエグゼキューターを必要としない場合。Rx ではブロックする必要があります。Rx は非同期にとらわれず、多くの場所で特別な方法で非同期を脅かさないため、それを回避する方法は他にありません。
.Subscribe(myAsyncHandler().Result)
ブロックしない場合、Rx はハンドラーがまだ非同期で実行されている間にアクションが完了したと見なします。
やったらこう思うかもしれません。
.ObserveOn(Scheduler.EventLoopSchedule)
問題は解決しました。ただし、これにより .Complete() ワークフローが中断されます。Rx は、実行をスケジュールするとすぐに完了したと見なし、非同期操作の完了を待たずにアプリケーションを終了するためです。
Rx よりも 4 つ以下の同時非同期タスクを許可する場合、すぐに使用できるものは何もありません。独自のスケジューラーやバッファーなどを実装することで、何かをハックできるかもしれません。
TPL Dataflow は、ActionBlock で非常に優れたソリューションを提供します。同時アクションを特定の数に絞り込むことができ、非同期操作を理解するので、Complete() を呼び出して Completed を待つことは、まさに期待どおりのことを行います: 進行中のすべての非同期タスクが完了するのを待ちます。
TPL が持つもう 1 つの機能は、「背圧」です。処理ルーチンでエラーを発見し、先月のデータを再計算する必要があるとします。Rx を使用してソースをサブスクライブし、パイプラインに無制限のバッファーまたは ObserveOn が含まれている場合、ソースは処理が処理できるよりも速く読み取り続けるため、数秒でメモリが不足します。ブロッキング コンシューマーを実装したとしても、ソースが非同期の場合など、ソースがブロッキング呼び出しに悩まされる可能性があります。TPLでは、ソースを次のように実装できます
while(...)
await actionBlock.SendAsync(msg)
ハンドラーがオーバーロードされている間、ソースをまだブロックしません。
全体として、Rx は時間と計算量が少ないアクションに適していることがわかりました。処理時間がかなりの量になると、奇妙な副作用と難解なデバッグの世界にいることになります。
良いニュースは、TPL Dataflow ブロックが Rx で非常にうまく機能することです。これらには AsObserver/AsObservable アダプターがあり、必要に応じて Rx パイプラインの途中に配置できます。しかし、Rx にはもっと多くのパターンとユースケースがあります。したがって、私の経験則では、Rx から始めて、必要に応じて TPL データフローを追加します。