web-dev-qa-db-ja.com

コンシューマー/プロデューサーとオブザーバー/オブザーバブルの違い

私は3つの部分で構成されるアプリケーションの設計に取り組んでいます。

  • 特定のイベントの発生(ファイルの作成、外部リクエストなど)を監視する単一のスレッド
  • これらのイベントを処理することによってこれらのイベントに応答するN個のワーカースレッド(各ワーカーは単一のイベントを処理して消費し、処理にはさまざまな時間がかかる場合があります)
  • それらのスレッドを管理し、エラー処理(スレッドの再起動、結果のロギング)を行うコントローラー

これはかなり基本的で、実装するのは難しくありませんが、それを行う「正しい」方法は何でしょうか(このJavaの具体的なケースでは、より高い抽象化の回答も評価されます)。 2つの戦略が思い浮かびます:

  • Observer/Observable:監視スレッドはコントローラーによって監視されます。イベントが発生した場合、コントローラーに通知され、再利用可能なキャッシュされたスレッドプールから空きスレッドに新しいタスクを割り当てることができます(または、すべてのスレッドの場合、タスクを待機してFIFOキューにキャッシュします)現在、ビジー状態です。ワーカースレッドはCallableを実装し、結果(またはブール値)で成功を返すか、エラーで戻ります。その場合、コントローラーは何をすべきかを決定します(発生したエラーの性質に応じて) )。

  • プロデューサー/コンシューマー:監視スレッドはコントローラー(イベントキュー)とBlockingQueueを共有し、コントローラーはすべてのワーカー(タスクキューと結果キュー)と2つを共有します。イベントの場合、監視スレッドはタスクオブジェクトをイベントキューに入れます。コントローラは、イベントキューから新しいタスクを取得し、それらを確認して、タスクキューに入れます。各ワーカーは新しいタスクを待ち、それらをタスクキュー(先着順、キュー自体が管理)から取得/消費し、結果またはエラーを結果キューに戻します。最後に、コントローラーは結果キューから結果を取得し、エラーが発生した場合に対応する手順を実行できます。

両方のアプローチの最終結果は似ていますが、それぞれわずかな違いがあります。

オブザーバーを使用すると、スレッドの制御が直接行われ、各タスクは特定の新しい生成されたワーカーに起因します。スレッド作成のオーバーヘッドは高くなる可能性がありますが、キャッシュされたスレッドプールのおかげではありません。一方、オブザーバーパターンは、複数ではなく単一のオブザーバーに削減されています。

キュー戦略は拡張がより簡単であるように見えます。たとえば、1つではなく複数のプロデューサーを追加するのは簡単であり、変更を必要としません。欠点は、まったく作業を行わなくても、すべてのスレッドが無期限に実行され、エラー/結果の処理が最初のソリューションほど洗練されていないことです。

この状況で最も適切なアプローチは何ですか?その理由は?ほとんどの例は、多くのウィンドウをObserverケースの新しい値で更新したり、複数のコンシューマーやプロデューサーで処理したりするなど、明確なケースしか扱っていないため、オンラインでこの質問に対する回答を見つけるのは難しいことに気づきました。どんな入力でも大歓迎です。

16
user183536

あなたはあなた自身の質問に答えるのにかなり近いです。 :)

Observable/Observerパターン(フリップに注意)では、3つの点に注意してください。

  1. 通常、変更の通知、つまり「ペイロード」は監視可能です。
  2. オブザーバブルが存在します
  3. オブザーバーはexistingオブザーバブルに認識されている必要があります(または、監視対象がありません)。

これらのポイントを組み合わせることにより、オブザーバブルはその下流のコンポーネント、つまりオブザーバーが何であるかを知っていることが暗示されます。データフローは本質的にオブザーバブルから駆動されます-オブザーバーは、観察している対象によって単に「生きて死ぬ」だけです。

Producer/Consumerパターンでは、非常に異なる相互作用が得られます。

  1. 一般に、ペイロードは、それを生成する責任があるプロデューサーとは無関係に存在します。
  2. プロデューサー方法や時期がわかりませんコンシューマーはアクティブです。
  3. 消費者はペイロードのプロデューサーを知る必要はありません。

これで、データフローはプロデューサーとコンシューマーの間で完全に切断されます。プロデューサーが知っているのは出力があることだけであり、コンシューマーが知っているのは入力があることだけです。重要なのは、これは生産者と消費者が他の存在なしに完全に存在できることを意味します。

別のそれほど微妙な違いは、sameオブザーバブルの複数のオブザーバーが通常同じペイロードを取得することです(従来とは異なる実装がない限り)、複数のコンシューマーsameプロデューサーはオフにできません。これは、仲介者がキューのようなアプローチかトピックのようなアプローチかによって異なります。前者はコンシューマごとに異なるメッセージを渡しますが、後者はすべてのコンシューマがメッセージごとに処理することを保証します(または試みます)。

それらをアプリケーションに適合させるには:

  • Observable/Observerパターンでは、監視スレッドが初期化されているときは常に、コントローラーに通知する方法を知っている必要があります。オブザーバーとして、コントローラーは監視スレッドからの通知を待ってから、スレッドに変更を処理させます。
  • Producer/Consumerパターンでは、監視スレッドはイベントキューの存在を知るだけでよく、それとのみ対話します。次に、コントローラーはコンシューマーとしてイベントキューをポーリングし、新しいペイロードを取得すると、スレッドにそれを処理させます。

したがって、質問に直接回答するには、監視スレッドとコントローラーをある程度分離して、それらを独立して操作できるようにする場合は、プロデューサー/コンシューマーパターンを使用する必要があります。

10
h.j.k.