キーワードのしくみawait
がわかりにくいので、少し理解を深めたいと思います。
それでも頭を回転させる問題は、再帰の使用です。次に例を示します。
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);
}
}
}
This one does not throw a StackOverflowException
. I can sortof see why it works, but I would call it more of a gut feeling (it probably deals with how the code is arranged to use callbacks to avoid building the stack, but I can't translate that gut feeling into an explanation)
So I have two questions:
- How does the second batch of code avoid a
StackOverflowException
? - Does the second batch of code waste other resources? (for example does it allocate an absurdly large number of Task objects on the heap?)
Thanks!