c# - 递归调用 async-void : any guarantee on the stack limit?

标签 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 代码的情况下)。只是怀疑循环何时非常紧密,以至于异步调用变得比整个函数本身“慢”。

最佳答案

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

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

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/

相关文章:

iPhone - 更改委托(delegate)可能会导致异步调用崩溃?

c++ - 如何将 std::future<T> 转换为 std::future<void>?

javascript - Node.js/Express 异步函数

javascript - 在循环递归期间,索引计数丢失

javascript - 使用 JavaScript 进行递归回文检查

c# - ASP.NET 中的 session 变量

c# - c++ 中的 string* [] 转换为 c# 中的 string[]

c# - 字符串中断反序列化中的空数组

Python - 快速排序 - 超过最大递归深度

c# - 从代码隐藏更改 IFrames InnerHtml