c# - 递归和 await/async 关键字

标签 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);
        }
    }
}

这显然会抛出一个 StackOverflowException

我的理解是因为代码实际上是同步运行的,直到第一个异步操作,之后它返回一个 Task 对象,其中包含有关异步操作的信息。在这种情况下,没有异步操作,因此它只是在最终会返回一个 Task 的错误 promise 下不断递归。

现在稍微改变一下:

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);
        }
    }
}

这个不会抛出 StackOverflowException。我可以sortof 明白它为什么有效,但我更愿意称之为直觉(它可能涉及如何安排代码以使用回调来避免构建堆栈,但我无法翻译那种直觉变成了一种解释)

所以我有两个问题:

  • 第二批代码如何避免StackOverflowException
  • 第二批代码是否浪费了其他资源? (例如,它是否在堆上分配了大量的 Task 对象?)

谢谢!

最佳答案

任何函数中直到第一个 await 的部分都是同步运行的。在第一种情况下,它会因此遇到堆栈溢出 - 没有任何东西会中断调用自身的函数。

第一个 await(不会立即完成 - 这很可能是您的情况)导致函数返回(并放弃其堆栈空间!)。它将其余部分作为延续进行排队。 TPL 确保延续永远不会嵌套得太深。如果存在堆栈溢出的风险,继续将排队到线程池,重置堆栈(开始填满)。

第二个示例仍然会溢出!如果 Task.Run 任务总是立即完成怎么办? (这不太可能,但通过正确的操作系统线程调度是可能的)。然后,异步函数将永远不会被中断(导致它返回并释放所有堆栈空间),并且会产生与情况 1 相同的行为。

关于c# - 递归和 await/async 关键字,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/13808166/

相关文章:

c# - 安全地对IoC/DI配置进行广泛的更改

c# - 如何使用 Entity Framework Core 在具有默认值的 boolean 值上设置另一个值?

javascript - typescript :可能导致此错误的原因是什么? "Element implicitly has an ' 任何类型,因为类型 'Object' 没有索引签名”

javascript - 如何使用 Promise 查看 setTimeout 中错误的完整堆栈跟踪

node.js - Nodejs 异步/等待嵌套 API 进度

javascript - 如何在vuejs中异步加载图片src url?

c# - 更改 DateTime 的时区以满足 API 契约(Contract)

c# - 使用另一个集合排序

python-3.x - 使用python递归返回列表列表

python - 递归子生成器调用不执行