我对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);
}
}
}
Run Code Online (Sandbox Code Playgroud)
这显然是一个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); …Run Code Online (Sandbox Code Playgroud) 这更多地出于好奇而不是任何现实世界的问题.
请考虑以下代码:
void Main()
{
FAsync().Wait();
}
async Task FAsync()
{
await Task.Yield();
await FAsync();
}
Run Code Online (Sandbox Code Playgroud)
在同步世界中,这最终会导致堆栈溢出.
在异步世界中,这只会消耗大量内存(我认为这与我可能松散地称之为"异步堆栈"的内容有关?)
这些数据到底是什么,以及如何保存?
我正在编写代码来解决问题,并且它适用于较小的输入,但是当增加到较大的输入时,我遇到了堆栈溢出异常:
Stack overflow.
Repeat 3239 times:
--------------------------------
at Program+<>c__DisplayClass0_0.<<Main>$>g__Traverse|3(Point, Direction)
--------------------------------
at Program.<<Main>$>g__Do|0_0()
Run Code Online (Sandbox Code Playgroud)
我还注意到,它声称重复的次数因执行而异,有时我也会得到 ~2300 次重复或 ~2800 次重复。
经过一段时间的调试后,我没有发现代码有任何问题,因此我手动将线程的堆栈大小设置为Int32.MaxValue,代码工作并产生了正确的答案。
我很好奇是否有人知道 C# 如何检测堆栈溢出?在这种情况下,在我看来,C# 过早地预测了堆栈溢出,即使程序确实有机会终止。
此外,为什么 C# 会给出堆栈溢出错误,指出调用已重复x多次,而x每次执行似乎都不同?
任何见解或信息表示赞赏!