面对这个奇怪的案例,我想知道这种模式是否会对堆栈使用产生任何保证(即可靠与否)。
注意:计数器只是“DoIt”函数中要做的一项假设工作。
class Program
{
static void Main(string[] args)
{
DoIt();
Console.ReadKey();
_flag = true;
}
private static bool _flag;
private static int _count;
private static async void DoIt()
{
await Task.Delay(1000);
_count++;
if (_flag) return;
DoIt();
}
}
“DoIt”任务应该永远调用自己,直到某些东西(即标志)破坏递归。我想知道堆栈是否填满,因为它反对“异步”调用,不应该停止调用者。
主要问题是:在某些情况下我可能会遇到 StackOverflow 异常,还是我保证不会发生异常?
如果我也删除延迟会怎么样?
至于我,该模式应该(几乎)始终是安全的,但老实说我不会解释如何保证它(至少在不分析背后的 IL 代码的情况下)。只是怀疑循环何时非常紧密,以至于异步调用变得比整个函数本身“慢”。
最佳答案
不,你最终会在很多任务上安排很多延续,而不是深度堆栈......至少在初始调用中是这样。
请注意,这只是因为您正在使用
await Task.Delay(1000);
... 可以合理地预期永远不会返回已经完成的任务。如果你有类似的东西:
var value = await cache.GetAsync(key);
它可能很容易返回一个已完成的任务,然后你真的可以得到一个非常深的堆栈。
您需要记住的是,调用是同步的,直到它遇到第一个 await
和 未完成 等待。
What if I also remove the delay?
然后您将有效地拥有一个递归调用自身的同步方法,并且除非很快设置标志真的,否则您将以堆栈溢出而告终。
请注意,即使您在当前代码中没有遇到堆栈溢出,您仍然每秒安排一个新任务,直到设置标志,这并不理想 - 它远非糟糕,因为任务无论如何都会很快消失,但如果可能的话我仍然会使用循环。
一个潜在的问题是,我相信“逻辑”堆栈仍然被捕获……并且随着您的进行,它会变得越来越大。不过,这可能只是调试时的情况 - 我不是这方面的专家。
关于c# - 递归调用 async-void : any guarantee on the stack limit?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/21461326/