私はawait
キーワードがどのように機能するかについて脆弱な理解を持っています。再帰とawait/asyncキーワード
私の頭が回転する問題は、再帰の使用です。ここに例があります:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace TestingAwaitOverflow
{
class Program
{
static void Main(string[] args)
{
var task = TestAsync(0);
System.Threading.Thread.Sleep(100000);
}
static async Task TestAsync(int count)
{
Console.WriteLine(count);
await TestAsync(count + 1);
}
}
}
これは明らかにStackOverflowException
をスローします。
私の理解は、非同期操作に関する情報を含むTask
オブジェクトを返す最初の非同期アクションまで、コードが実際に同期して実行されるためです。この場合、非同期操作はありません。したがって、最終的にTask
が返されるという誤った約束の下で再帰し続けます。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace TestingAwaitOverflow
{
class Program
{
static void Main(string[] args)
{
var task = TestAsync(0);
System.Threading.Thread.Sleep(100000);
}
static async Task TestAsync(int count)
{
await Task.Run(() => Console.WriteLine(count));
await TestAsync(count + 1);
}
}
}
この1つはStackOverflowException
をスローしません。私はsortofを参照してください、しかし、私はそれが腸の感情(それはおそらくスタックのビルドを避けるためにコールバックを使用するように配置されているコードを扱うだろうが、私はその腸の感情を説明)
だから、私は2つの質問があります:コードの第二のバッチは、StackOverflowException
を避けるどのよう
- を?
- コードの2番目のバッチは他のリソースを浪費しますか? (例えば、ヒープ上に不当に多数のタスクオブジェクトを割り当てていますか?)
ありがとう!
もし 'Task.Run()'を直ちに完了した 'Task'オブジェクトを返す関数に置き換えると、スタックオーバーフロー例外が再導入されますか? (あるいは、私はできないものを提案しただけですか?) – riwalk
@ Stargazer712それは可能であるだけでなく、あなたが描いていることを確かにするでしょう。単に 'await Task.FromResult
@ Stargazer712はい! Task.Yield()(これは継続をポストする必要があることが保証されています)で置き換えると、(パフォーマンスコストで)スタックのオーバーフローを防ぐことができます。 'Yield'は' Task'以外の待ち時間を返します。待機中の機能によって照会されたときに完了しないようにする必要があるため、この保証をタスクから取得することははるかに難しくなります。 – usr