SubscribeOn(Scheduler.TaskPool)を使用しているときにRxでOnNext呼び出しが重複しないようにする

私はいくつかのコードをRxを使用して、複数のスレッドから呼び出されます:

subject.OnNext(value);//where subject is Subject

私は値をバックグラウンドで処理したいので、私のサブスクリプションは

subscription = subject.ObserveOn(Scheduler.TaskPool).Subscribe(value =>
{
   //use value
});

作業がTaskPoolに入れられ、現在のスレッドをブロックしない限り、Observableから出てくる値を処理するスレッドは本当に気にしません。しかし、私のOnNextデリゲート内での 'value'の使用は、スレッドセーフではありません。現時点でObservableを介して多くの値が渡っている場合、私はOnNextハンドラへの呼び出しが重複しています。

私はOnNextデリゲートにロックを追加することができますが、それはRxのやり方のようには感じられません。 subject.OnNext(value); を呼び出す複数のスレッドがあるときに、一度に1つのOnNextハンドラを呼び出すようにするにはどうすればよいでしょうか?

11

4 答え

MSDNでの件名の使用

デフォルトでは、サブジェクトは   スレッド。 [...]しかし、あなたがしたい場合   スケジューラを使用して発信者をオブザーバに同期させると、   これを行うにはSynchronizeメソッドを使用します。

だから、Brandonがコメントで言うように、主題を同期させて、それをあなたのプロデューサースレッドに渡すべきです。例えば

var syncSubject = Subject.Synchronize(subject);

// syncSubject.OnNext(value) can be used from multiple threads

subscription = syncSubject.ObserveOn(TaskPoolScheduler.Default).Subscribe(value =>
{
   //use value
});
11
追加された
ObserveOn の並行処理規約に違反している場合は、 Synchronize の呼び出しが ObserveOn の呼び出しの前である必要があります。しかし実際には、ユースケースが複数のスレッド間でサブジェクトを共有している場合、サブスクライバをサブジェクトと同期させるのではなく、サブジェクトを同期させるのが最善の解決策です。 var syncSubject = Subject.Synchronize(syncSubject); syncSubject をプロデューサスレッドに渡すと、元のサブジェクトのサブスクライバに問題を引き起こさずに syncSubject.OnNext()を呼び出すことができます。
追加された 著者 Brandon,
@Beachwalkerはタイプミスです。回答に示されているように、それはSynchronizeの引数として渡される subject でなければなりません
追加された 著者 Brandon,
@Brandon var syncSubject =サブジェクト同期(syncSubject); ...割り当てられる前に変数を使用していますが、これを明確にすることはできますか?
追加された 著者 Beachwalker,

私はあなたが.Synchronize()拡張メソッドを探していると思います。最近のリリース(2011年後半)のパフォーマンス改善を得るために、Rxチームは観察可能なシーケンスプロデューサの連続性に関する仮定を緩和しました。しかし、あなたはこれらの前提を崩しているようです(悪いことではありません)が、ユーザーが期待するようにRxを再生させるためには、シーケンスを同期させて再度シーケンシャルにする必要があります。

5
追加された

OnNextメソッドでロックを適用する独自のIObserverを実装しようとすることができます。単純なデコレータ:ロックを適用し、内部のOnNextを呼び出してロックを削除します。

次に、.AsThreadSafe()のようなIObservableに拡張メソッドを実装できます。

2
追加された
最高の解決策ではありませんが、それが有効な解決策であるため、私の視点から下降させる必要はありません。だから私は+1をここでdownvotesと少しのバランスをとる。
追加された 著者 Beachwalker,