少し問題がありました。:)
常に N スレッドのみがバックグラウンド タスクを実行するようにしたいと考えています。これを行うために、固定スレッド プール エグゼキュータを使用しました。うまく機能しているように見えました。
その後、問題が見つかりました。エグゼキューターを使用していくつかの並列作業を行うクラスがあり、エグゼキューター スレッド内で別のクラスを呼び出し、そのスレッドも並列作業を行い、それを待機しようとしているとします。何が起こるかは次のとおりです。
- メイン スレッドは、第 1 レベルのメソッドを呼び出します。
- このメソッドは、16 のタスクに並列化できると考え、作業を分割します。
- 16 個のタスクがエグゼキューターにサブミットされます。
- メイン スレッドは、そのタスクが完了するのを待機し始めます。
- 利用可能なスレッドが 4 つあるとすると、最初の 4 つのタスクがそれぞれ選択されて実行されます。したがって、キューには 12 個のタスクが残っています。
- ここで、これらのタスクの 1 つが他のメソッドを呼び出します。
- この新しい方法は、2 つのタスクに並列化できると考えています。それが並列マージソートの最初のステップであるか、それらの線に沿った何かであるとしましょう。
- 2 つのタスクがエグゼキューターにサブミットされます。
- このスレッドは、タスクが完了するのを待ち始めます。
ええとああ。そのため、この時点で、4 つのスレッドすべてがタスクの完了を待機していますが、これらのタスクを実際に実行しているエグゼキュータを共同でブロックしています。
この問題の解決策 1 は次のとおりです。エグゼキューターに新しいタスクを送信するときに、すべてのスレッドを既に実行しており、エグゼキューター スレッドの 1 つで既に実行している場合は、タスクをインラインで実行します。これは 10 か月間問題なく機能していましたが、現在問題が発生しています。サブミットしている新しいタスクがまだ比較的大きい場合、メソッドが他のタスクをキューに追加することを新しいタスクがブロックする状況に陥る可能性があります。そうしないと、他のワーカー スレッドによって取得される可能性があります。そのため、スレッドが作業をインラインで処理している間、かなりの遅延が発生します。
バックグラウンド タスクの潜在的に無限のツリーを実行するという中心的な問題に対するより良い解決策はありますか? executor サービスに相当する .NET には、キューから盗む何らかの組み込み機能があり、元のデッドロックの問題が発生するのを防ぎます。これは、私が知る限り、理想的なソリューションです。しかし、Java の土地ではどうでしょうか。