async関数のリターンサイトは呼び出し元ではないので、これは機能すると思いますが、念のためこれが安全であることを確認したいと思いました。そうでない場合、なぜこれがスタックをオーバーフローさせるのでしょうか?
static async Task CheckAsync(TimeSpan recursiveTimer)
{
// do some work
await Task.Delay(recursiveTimer);
CheckAsync(recursiveTimer);
}
編集:私はそれを試してみることにしました-それはスタックをオーバーフローしていないようです(それは今私のマシンで実行されています-それは現在210,000を呼び出しています)。私が推測した理由は、CheckAsync関数の戻りサイトが実際にはCheckAsyncではなく、非同期配管のどこかにあるためです。したがって、CheckAsyncがCheckAsyncを呼び出すとき、実際には通常の関数呼び出しメカニズムを介して呼び出しスタックに追加されるのではなく、非同期関数を管理する他のスレッドを介して実行される非同期の「実行される」キューに関数をオブジェクトとして配置します。
このメカニズムをよく知っている人には、これは正しいと思いますか?
それがあなたのために働いている理由は、CheckAsync
呼ばれる方法のためではなく、あなたがの結果を待っているからですTask.Delay
。それは常に「まだ完了していない」タスクを返すので、それを待つと継続がスケジュールされます。その継続は事実上空のスタックで実行されるため、再帰呼び出しを行うことは重要ではありません。
さて、IIRCフレームワークはどんどん大きくなる「論理スタック」を追跡するので、まだ効果的にメモリリークがあると思います...しかし、それはヒープに保存され、不足するまで拡張されますメモリ。
スタックが爆発するのを見たい場合は、コードを次のように変更するだけです。
static async Task CheckAsync(TimeSpan recursiveTimer)
{
// Whatever
await Task.FromResult(5);
CheckAsync(recursiveTimer);
}
その時点で、「whatever」のコードが何も待たないと仮定するTask
と、完了と例外を追跡するために使用するだけで、完全に同期したコードになります。
これを繰り返し作業を行うためのパターンとしてはお勧めしませんが(前述のメモリリークのせいもあります)、スタックオーバーフローが発生しない理由を説明できると思います。
この記事はインターネットから収集されたものであり、転載の際にはソースを示してください。
侵害の場合は、連絡してください[email protected]
コメントを追加