递归调用async-void:堆栈限制的任何保证?

时间:2014-01-30 15:59:06

标签: c# asynchronous recursion async-await stack-overflow

只是面对这个奇怪的情况,我想知道这种模式是否会对堆栈使用产生任何保证(即可靠或不可靠)。

注意:计数器只是“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代码)。只是怀疑当循环非常紧密,以便异步调用比整个函数本身“慢”。

1 个答案:

答案 0 :(得分:4)

不,你最终会在很多任务上安排很多延续,而不是深层堆叠...至少在初始调用中。

请注意,这只是 ,因为您正在使用

await Task.Delay(1000);

......可以合理地预期永远不会返回已经完成的任务。如果你有类似的东西:

var value = await cache.GetAsync(key);

它可能很容易返回已完成的任务,那么你最终可能会得到一个非常深的堆栈。

您需要记住的是,只有在遇到未完成的第一个await时,该呼叫才会同步。

  

如果我也删除延迟怎么办?

然后你有效地拥有一个递归调用自身的同步方法,除非标志很快被设置为 ,否则最终会出现堆栈溢出。

请注意,即使你没有在当前代码中获得堆栈溢出,你仍然每秒安排一个新任务,直到设置了标志,这是不理想的 - 它远非糟糕,因为任务无论如何都会快速消亡,但如果可能的话,我仍会使用循环。

一个潜在的问题是我相信“逻辑”堆栈仍然被捕获......随着你的进展,这将变得越来越大。这可能只是在调试的情况下 - 我不是它的专家。