TPLではなくRxを使用することを選択するのはいつですか、それとも2つのフレームワークは直交していますか?
私が理解していることから、Rxは主にイベントの抽象化を提供し、構成を可能にすることを目的としていますが、非同期操作の抽象化も可能にします。 CreatexxオーバーロードとFromxxxオーバーロードを使用し、返されたIDisposableを破棄してキャンセルします。
TPLは、タスクおよびキャンセル機能を介した操作の抽象化も提供します。
私のジレンマは、いつ、どのシナリオで使用するかです。
Rxの主な目的は、イベントの抽象化を提供することではありません。これはその成果の1つにすぎません。その主な目的は、コレクションに構成可能なプッシュモデルを提供することです。
リアクティブフレームワーク(Rx)は、IObservable<T>
の数学的二重であるIEnumerable<T>
に基づいています。したがって、IEnumerable<T>
を使用してコレクションからアイテムを「プル」するのではなく、IObservable<T>
を介してオブジェクトを「プッシュ」することができます。
もちろん、実際に観察可能なソースを探しに行くときは、イベントや非同期操作などが優れた候補です。
リアクティブフレームワークでは、当然、監視可能なデータのソースを監視し、クエリとサブスクリプションを管理できるマルチスレッドモデルが必要です。 Rxは実際にこれを行うためにTPLを多用します。
したがって、Rxを使用する場合は、暗黙的にTPLを使用しています。
タスクを直接制御したい場合は、TPLを直接使用します。
ただし、監視してクエリを実行したいデータのソースがある場合は、リアクティブフレームワークを徹底的にお勧めします。
私が従うのが好きないくつかのガイドライン:
更新、2016年12月:30分ある場合は、私の推測ではなく、JoeDuffyの直接のアカウントを読むことをお勧めします。私の分析はうまくいくと思いますが、この質問を見つけた場合は、TPL vs Rx.NETに加えて、MS研究プロジェクト(Midori、Cosmos)もカバーしているため、これらの回答の代わりにブログ投稿を参照することを強くお勧めします。
http://joeduffyblog.com/2016/11/30/15-years-of-concurrency/
.NET 2.0がリリースされた後、MSは過剰修正に大きな間違いを犯したと思います。彼らは、会社のさまざまな部分から同時に多くの異なる同時実行管理APIを導入しました。
Future<T>
_として始まり、_Task<T>
_に変わった)をスレッドセーフなプリミティブに置き換えることを強く求めていました。その間、多くのマネージドAPIチームはAPMとThreadpool.QueueUserWorkItem()
を使用しようとしていましたが、Toubがmscorlib.dllで_Future<T>
_/_Task<T>
_を出荷するための戦いに勝つかどうかはわかりませんでした。結局、彼らはヘッジし、mscorlibで_Task<T>
_と_IObservable<T>
_の両方を出荷したように見えますが、mscorlibで他のRx API(_ISubject<T>
_さえも)を許可しませんでした。この生け垣は、(後で)膨大な量の重複を引き起こし、社内外の努力を無駄にしたと思います。
複製については、Task
vs. _IObservable<Unit>
_、_Task<T>
_ vs. _AsyncSubject<T>
_、Task.Run()
vs. Observable.Start()
を参照してください。そして、これは氷山の一角にすぎません。しかし、より高いレベルでは、次のことを考慮してください。
IEnumerable
スタイルの拡張メソッドと混合します。これは、永久に非常に簡単にブロックできることを意味します(ホットストリームでFirst()
を呼び出しても戻りません)。スケジューリング制限(並列処理の制限)は、かなり奇妙なSubscribeOn()
拡張メソッドを介して行われます。これは、奇妙なことに暗黙的であり、正しく理解するのが困難です。 Rxの学習を開始する場合は、回避すべきすべての落とし穴を学習するために長い時間を確保してください。ただし、複雑なイベントストリームを作成する場合、または複雑なフィルタリング/クエリが必要な場合は、Rxが実際に唯一のオプションです。MSがmscorlibで_ISubject<T>
_を出荷するまで、Rxが広く採用される可能性はないと思います。 Rxには_TimeInterval<T>
_や_Timestamped<T>
_などの非常に便利な具象(ジェネリック)型が含まれているため、これは悲しいことです。これらは_Nullable<T>
_のようにCore/mscorlibにあるはずです。また、_System.Reactive.EventPattern<TEventArgs>
_。
スコットWの箇条書きが好きです。 Rxマップにいくつかのより具体的な例を入れると、
TPLはうまくマッピングされているようです
IObservable(Rx)で私が気づいたことの1つは、それが普及することです。コードベースに入ると、間違いなく他のインターフェイスを介して公開されるため、最終的にはアプリケーション全体に表示されます。これは最初は怖いかもしれませんが、チームのほとんどは現在Rxにかなり慣れており、Rxによって節約できる作業量が大好きです。
IMHO Rxは、.NET 3.5、4.0、Silverlight 3、Silverlight 4、およびJavascriptですでにサポートされているため、TPLよりも主要なライブラリになります。これは、効果的に1つのスタイルを学ぶ必要があり、多くのプラットフォームに適用できることを意味します。
[〜#〜] edit [〜#〜]:RxがTPLよりも支配的であるという考えを変えました。それらはさまざまな問題を解決するので、実際にはそのように比較するべきではありません。 .NET 4.5/C#5.0では、async/awaitキーワードがさらにTPLに結び付けられます(これは良いことです)。 Rx vsイベントvs TPLなどに関する深い議論については、私のオンラインブックの 最初の章 をチェックしてください IntroToRx.com
TPL Dataflowは、Rxの機能の特殊なサブセットをカバーしていると言えます。 Dataflowは、測定可能な時間がかかる可能性のあるデータ処理用ですが、Rxは、マウスの位置やエラー状態など、処理時間が無視できるイベント用です。
例:「サブスクライブ」ハンドラーは非同期であり、一度に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は時間と計算が軽いアクションに適していることがわかりました。処理時間がかなり長くなると、奇妙な副作用と難解なデバッグの世界にいることになります。
良いニュースは、TPLDataflowブロックがRxで非常にうまく機能することです。 AsObserver/AsObservableアダプターがあり、必要に応じてRxパイプラインの途中に貼り付けることができます。しかし、Rxにははるかに多くのパターンとユースケースがあります。したがって、私の経験則では、Rxから始めて、必要に応じてTPLデータフローを追加します。