私は、次の3つの部分で構成されるアプリケーションの設計に取り組んでいます。
- 特定のイベントの発生(ファイルの作成、外部リクエストなど)を監視する単一のスレッド
- これらのイベントを処理することでこれらのイベントに応答するN個のワーカースレッド(各ワーカーは単一のイベントを処理して消費し、処理に時間がかかる場合があります)
- これらのスレッドを管理し、エラー処理を行うコントローラー(スレッドの再起動、結果のログ記録)
これは非常に基本的で実装するのは難しくありませんが、それを行うための「正しい」方法は何だろうと思っています(この具体的なケースではJavaですが、より高い抽象化の答えもありがたいです)。2つの戦略が思い浮かびます。
Observer / Observable:監視スレッドはコントローラーによって監視されます。イベントが発生した場合、コントローラーに通知され、再利用可能なキャッシュスレッドプールから新しいタスクを空きスレッドに割り当てることができます(または、すべてのスレッドが現在ビジーである場合、FIFOキューでタスクを待機してキャッシュします)。ワーカースレッドはCallableを実装し、結果(またはブール値)で成功を返すか、エラーを返します。その場合、コントローラーは何をすべきかを決定します(発生したエラーの性質に応じて)。
プロデューサー/コンシューマー:監視スレッドはコントローラーとBlockingQueueを共有し(イベントキュー)、コントローラーはすべてのワーカーと2つを共有します(タスクキューと結果キュー)。イベントの場合、監視スレッドはタスクオブジェクトをイベントキューに入れます。コントローラーは、イベントキューから新しいタスクを取得し、それらをレビューして、タスクキューに入れます。各ワーカーは新しいタスクを待機し、タスクキュー(先着順、キュー自体で管理)からそれらを取得/消費し、結果またはエラーを結果キューに戻します。最後に、コントローラーは結果キューから結果を取得し、エラーが発生した場合に対応する手順を実行できます。
両方のアプローチの最終結果は似ていますが、それぞれわずかな違いがあります。
オブザーバーを使用すると、スレッドの制御は直接行われ、各タスクは特定の新しく生成されたワーカーに割り当てられます。スレッド作成のオーバーヘッドは高くなる可能性がありますが、キャッシュされたスレッドプールのおかげではありません。一方、Observerパターンは、複数ではなく単一のObserverに縮小されます。これは、厳密に設計されたものではありません。
キュー戦略は拡張が容易なようです。たとえば、1つではなく複数のプロデューサーを追加するのは簡単で、変更を必要としません。欠点は、作業をまったく行わない場合でも、すべてのスレッドが無期限に実行され、エラー/結果の処理が最初のソリューションほど洗練されていないことです。
この状況で最もふさわしいアプローチは何ですか?その理由は?ほとんどの例は、Observerケースの新しい値で多くのウィンドウを更新したり、複数のコンシューマーとプロデューサーで処理したりするなど、明確なケースのみを扱っているため、この質問に対する答えをオンラインで見つけるのは難しいと感じました。どんな入力でも大歓迎です。