18

Java7で導入されたFork/Joinフレームワークの実装について読みましたが、魔法がどのように機能するかを理解していることを確認したかっただけです。

私が理解しているように、スレッドがフォークすると、キューにサブタスクが作成されます(他のスレッドが盗む場合と盗まない場合があります)。スレッドが「参加」しようとすると、実際にキューで既存のタスクをチェックしてから再帰的に実行します。つまり、「参加」操作では、2つのフレームがスレッド呼び出しスタックに追加されます(1つは参加用、もう1つは1つ)。新しく実行されたタスクの呼び出し用)。

JVMは末尾呼び出しの最適化(この状況でjoinメソッドのスタックフレームを削除するのに役立つ可能性があります)をサポートしていないことを知っているので、多くのフォークと結合で複雑な操作を実行すると、スレッドがスローされる可能性があると思いますStackOverflowError

私は正しいですか、それとも彼らはそれを防ぐためのいくつかのクールな方法を見つけましたか?

編集

質問を明確にするのに役立つシナリオを次に示します。(簡単にするために)フォークジョインプールにはスレッドが1つしかないことを伝えます。ある時点で、スレッドはフォークしてからjoinを呼び出します。joinメソッドを使用している間、スレッドは(キューで見つかったように)フォークされたタスクを実行できることを検出し、次のタスクを呼び出します。このタスクは次にフォークしてからjoinを呼び出します。そのため、joinメソッドを実行している間、スレッドは(前と同様に)キュー内でフォークされたタスクを見つけて呼び出します。その段階では、コールスタックには少なくとも2つの結合と2つのタスクのフレームが含まれます。

ご覧のとおり、フォーク結合フレームワークは単純な再帰に変換されています。Javaは末尾呼び出しの最適化をサポートしていないため、Javaでのすべての再帰は、StackOverflowError十分に深くなると発生する可能性があります。

私の質問は-fork/joinフレームワークの実装者は、この状況を防ぐためのクールな方法を見つけましたか?

4

3 に答える 3

8

残念ながら、スレッド再帰スタックに関して魔法のようなことは何も起きていません。最初のタスクが fork/split し、妥当な解決ポイントがない場合、StackOverflowErrors が発生します。

おそらく、JavaDoc のチュートリアルで各サブタスクが半分に分割されている理由を理解できるでしょう。

于 2012-07-05T21:26:04.150 に答える
2

通常、スタックにプッシュされる新しいタスクはそれぞれ、前のタスクの半分のサイズになります。そのため、作業量はスタック サイズとともに指数関数的に増加します。小さなスタックでも、しばらく忙しくするのに十分な量の仕事を収めることができます。

于 2012-06-29T14:15:08.693 に答える
1

私はあなたを正しい方法で理解することを願っています。

forkjoinpool には、実行したいタスクを保持する内部キューがあるため、スタック オーバーフローがスローされることはありませんが、メモリ使用率が高くなるように準備する必要があります。

fork メソッドの非常に興味深い場所は、安全でないオブジェクトの使用法を持つ ForkJoinWorkerThread.pushTask であるため、タスクの格納に配列が使用されていることに注意する必要があります。

編集: 最初に簡単です - キューの一番上にいるときは、単純にアンプッシュされて実行され、結果が返されます。(forkjointask.java:353)

依存関係がある場合は別のアプローチが使用されます。この場合、制御は WorkerThread に返され、WorkerThread はチェーンの検出と実行を担当します。最初のワーカーによって、未処理のタスクがないかローカル キューをチェックし、そのようなタスクがない場合は、渡されたジョブを実行して結果を返します。それ以外の場合は、次のケースに進みます。これは、スティーラーを数回助けています。何も役に立ちません...最初のステップでMAX_HELPに等しい再試行は現在ゼロです-制御がプールに渡され、いくつかのチェックが実行され、tryAwaitDoneが実行されます。そして、このメソッドで wait が呼び出され、タスクの完了を待ちます。

これは、fork join pool がいくつかのステップで終了し、wait の呼び出しを回避することで速度と時間を最適化しようとすることを意味します。ただし、待機中に終了する可能性があります。これは、非常にコストのかかる同期プロセスを開始することを意味します。

したがって、無限の深さの後続の結合はありませんが、タスクをできるだけ速く実行するための論理的な試行があります。

于 2012-07-05T17:53:37.183 に答える