由于async函数的返回站点不是调用者,我认为这有效,但我认为我会验证这是安全的以防万一。如果不是,为什么会溢出堆栈呢?
static async Task CheckAsync(TimeSpan recursiveTimer)
{
// do some work
await Task.Delay(recursiveTimer);
CheckAsync(recursiveTimer);
}
编辑: 我决定尝试一下 - 看起来它不会溢出堆栈(它现在在我的机器上运行 - 它目前正在调用210,000)。我假设的原因是因为CheckAsync函数的返回站点实际上不是CheckAsync,而是异步管道中的某个地方。因此,当CheckAsync调用CheckAsync时,它实际上并不是通过普通函数调用机制添加到调用堆栈,而是将该函数作为对象放在某个异步“待执行”队列中,该队列通过管理异步函数的其他线程运行。
对于任何了解这种机制的人:这听起来是对的吗?
答案 0 :(得分:5)
它为你工作的原因不是因为调用CheckAsync
的方式,而是因为你正在等待Task.Delay
的结果。这将永远回归"尚未完成"任务,所以等待它将安排继续。该延续将在有效的空堆栈上触发,因此您进行递归调用并不重要。
现在,我认为你仍然有效地存在内存泄漏,因为IIRC框架将跟踪一个"逻辑堆栈"它将变得越来越大......但是它将被存储在堆上并扩展直到你的内存不足。
如果你想看到堆栈爆炸,你需要做的就是将代码更改为:
static async Task CheckAsync(TimeSpan recursiveTimer)
{
// Whatever
await Task.FromResult(5);
CheckAsync(recursiveTimer);
}
此时,假设代码在"无论什么"没有等待任何事情,您将拥有完全同步的代码,只需使用Task
来跟踪完成和异常。
我当然不会推荐这个作为重复工作的模式(部分是由于我提到的内存泄漏),但我希望这能解释为什么你没有得到堆栈溢出。
答案 1 :(得分:1)
溢出的原因是溢出堆栈。
10 static async Task CheckAsync(TimeSpan recursiveTimer)
20 {
30 // do some work
40 await Task.Delay(recursiveTimer);
50 CheckAsync(recursiveTimer);
60 }
代码执行将
10 20 30 40 50 60 //CheckAsync(recursiveTimer)
10 20 30 40 50 60 //CheckAsync(CheckAsync(recursiveTimer))
10 20 30 40 50 60 //CheckAsync(CheckAsync(CheckAsync(recursiveTimer))
该方法的内容应该是:
while(doCheck){
await Task.Delay(recursiveTimer);
CheckAsync(recursiveTimer);
}
假设你想在每次异步调用后等待,并在 doCheck 为真时执行此操作。我假设您在另一个帖子中更改 doCheck 值。