riw*_*alk 29 c# recursion async-await
我对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);
}
static async Task TestAsync(int count)
{
await Task.Run(() => Console.WriteLine(count));
await TestAsync(count + 1);
}
}
}
Run Code Online (Sandbox Code Playgroud)
这一个不扔StackOverflowException.我可以理解为什么它有效,但我会更多地称之为直觉(它可能涉及代码如何安排使用回调以避免构建堆栈,但我无法将这种直觉转化为解释)
所以我有两个问题:
StackOverflowException?谢谢!
usr*_*usr 17
任何函数中第一个等待的部分同步运行.在第一种情况下,由于这种情况,它会遇到堆栈溢出 - 没有任何事情会中断调用自身的函数.
第一个await(它没有立即完成 - 对于你有很高可能性的情况)导致函数返回(并放弃它的堆栈空间!).它将其余部分排成一个延续.TPL确保延续从不嵌套太深.如果存在堆栈溢出的风险,则继续将排队到线程池,重置堆栈(它开始填满).
第二个例子仍然可以溢出!如果Task.Run任务总是立即完成怎么办?(这不太可能,但可以使用正确的OS线程调度).然后,async函数永远不会被中断(导致它返回并释放所有堆栈空间),并且会产生与情况1相同的行为.
| 归档时间: |
|
| 查看次数: |
6278 次 |
| 最近记录: |