.NET 4.8 中的异步等待递归导致 StackoverflowException(.Net Core 3.1 中没有!)

2023-12-31

为什么下面的代码在.Net4.8中只有17深度递归会导致StackOverflowException?然而,这在 NetCore 3.1 中不会发生(我可以将计数设置为 10_000,它仍然有效)

class Program
{
  static async Task Main(string[] args)
  {
    try
    {
      await TestAsync(17);
    }
    catch(Exception e)
    {
      Console.WriteLine("Exception caught: " + e);
    }
  }

  static async Task TestAsync(int count)
  {
    await Task.Run(() =>
    {
      if (count <= 0)
        throw new Exception("ex");
    });

    Console.WriteLine(count);
    await TestAsync2(count);
  }

  static async Task TestAsync2(int count) => await TestAsync3(count);
  static async Task TestAsync3(int count) => await TestAsync4(count);
  static async Task TestAsync4(int count) => await TestAsync5(count);
  static async Task TestAsync5(int count) => await TestAsync6(count);
  static async Task TestAsync6(int count) => await TestAsync(count - 1);
}

这是 .Net 4.8 中的已知错误吗?我会在这样的函数中排除超过 17 级的递归...这是否实际上意味着不建议使用 async/await 编写递归?

更新:简化版

class Program
{
  // needs to be compiled as AnyCpu Prefer 64-bit
  static async Task Main(string[] args)
  {
    try
    {
      await TestAsync(97); // 96 still works
    }
    catch(Exception e)
    {
      Console.WriteLine("Exception caught: " + e);
    }
  }

  static async Task TestAsync(int count)
  {
    await Task.Run(() =>
    {
      if (count <= 0)
        throw new Exception("ex");
    });

    Console.WriteLine(count);
    await TestAsync(count-1);
  }
}

只有在选择的时候它才会发生得那么快Any Cpu与首选32 位禁用,但可以在多个 .net 版本(.Net 4.7.2 和 .Net 4.8)上的多台计算机(Windows 1903 和 1909)上重现


我怀疑您正在看到 Stack Overflow完成情况- 即,每个数字都会一直打印到1在堆栈溢出消息之前。

我的猜测是这种行为是因为await使用同步延续 https://blog.stephencleary.com/2012/12/dont-block-in-asynchronous-code.html。有supposed是防止同步延续溢出堆栈的代码 https://devblogs.microsoft.com/pfxteam/when-executesynchronously-doesnt-execute-synchronously/,但它是启发式的,并不总是有效。

我怀疑这种行为不会发生在 .NET Core 上,因为大量的优化工作已经投入到 .NET Core 中async支持,可能意味着该平台上的延续占用更少的堆栈空间,从而使启发式检查起作用。启发式本身也可能已在 .NET Core 中修复。无论哪种方式,我都不会屏息期待 .NET Framework 获得这些更新。

我会在这样的函数中排除超过 17 级的递归......

不是真的 17. 你有 102 级递归(17 * 6)。测量actual占用的堆栈空间是17 * 6 * (number of stacks to resume continuations)。在我的机器上,17 个有效;它在超过 200 个地方失败(1200 个深度调用)。

请记住,这只发生在长序列中尾递归异步函数 - 即,它们都没有任何异步工作要做after their await。如果您更改任何函数以进行其他异步工作after他们的递归await,这将避免堆栈溢出:

static async Task TestAsync(int count)
{
  await Task.Run(() =>
  {
    if (count <= 0)
      throw new Exception("ex");
  });

  Console.WriteLine(count);
  try
  {
    await TestAsync2(count);
  }
  finally
  {
    await Task.Yield(); // some other async work
  }
}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

.NET 4.8 中的异步等待递归导致 StackoverflowException(.Net Core 3.1 中没有!) 的相关文章

随机推荐

  • 如何在C#中使用WCF REST服务?

    我的合同详细信息如下 我使用 Json 响应和请求格式 还使用 POST 方法 如何用 C 编写客户端来使用此服务 OperationContract WebInvoke UriTemplate RESTJson Sample1 Sampl
  • Firebase 身份验证 Javascript:用于重定向待处理凭据的 setCookie

    我正在尝试使用多重身份验证进行链接singinwithredirect 我抓住了错误 credential并使用JSON stringify通过 cookie 传递它 从那里 我使用以下方法将其转换回对象JSON parse 但是当我尝试将
  • 富文本 (YUI) 编辑器在 IE11 上损坏

    我正在运行 Internet Explorer 11 和 YUI 2 富文本编辑器似乎无法正常工作 有关更多详细信息 请参阅随附的屏幕截图 有什么想法如何在 IE11 下解决这个问题吗 由于IE改变了它的User Agent YUI 2 9
  • Android Studio 初始化“com.intellij.util.net.ssl.CertificateManager”时发生致命错误

    当我将Android Studio 3 0升级到3 1时出现这个问题 java lang RuntimeException com intellij ide plugins PluginManager StartupAbortedExcep
  • 使用 Python 检测 C 文件中的递归

    我需要检测相当大 5 15 000 的 C 不是 C 文件集中的直接和间接递归 文件已经过预处理 出于安全原因 该代码相当 老派 因此没有诸如函数指针之类的奇特东西 只有传递变量的函数和一些执行相同操作的函数宏 检测递归的最自然的方法是创建
  • 实体框架-“不允许新事务,因为会话中还有其他线程正在运行”

    我在尝试保存实体框架中的更改时收到以下错误 System Data SqlClient SqlException 不允许新事务 因为会话中还有其他线程正在运行 我已经看到了这个问题的各种答案 但我似乎无法让它们中的任何一个工作 基本上我在存
  • 增加 Azure Api 应用程序中的最大请求长度

    我创建了一个 Azure Api 应用程序 将用它来上传文件 这些文件将 gt 4mb 因此需要增加最大请求长度 我已将以下内容添加到 Web config
  • 取消 UNNotificationRequest

    Because UILocalNotification现已弃用 我将代码移至新版本UNNotificationRequest API 它指出 取消本地通知 在 iOS 10 0 中已弃用 使用 UserNotifications Frame
  • 缺少 git 提交

    在工作中 这种情况经常发生 有人不小心将一些东西提交到 master 而不是预期的功能分支 然后这个人尝试解决它 结果却突然消失了 我进行了仔细的搜索 但找不到任何文档来解释为什么会发生这种情况 或者如何纠正这种情况 以下是重现步骤 git
  • 尽管有有效的 SSH 密钥,仍无法将 git 存储库推送到 Heroku

    有很多堆栈文章引用了与我收到的相同的错误消息 我在另一台计算机上浏览了整个 heroku 设置文档 一切都运行良好 不知道为什么这个不起作用 但我需要它 当我跑步时 gt git push v heroku master Pushing t
  • 在 Html.ActionLink 中添加图像

    我试图创建一个选项来在 ASP net MVC 带有剃刀视图引擎 中的列表视图和小部件视图之间切换 但是 我在尝试添加图像并将其缩放到 正确的高度 与其旁边的高度相同 时遇到了一些麻烦 我一直在寻找创建类似的东西 期望的结果 List Vi
  • IIS7 中的“经典”和“集成”管道模式有什么区别?

    昨晚我正在部署一个 ASP NET MVC 应用程序 发现将 IIS7 设置为集成模式进行部署会减少工作量 我的问题是有什么区别 使用其中一种或另一种会有什么影响 经典模式 IIS6及以下版本中的唯一模式 是IIS仅直接与ISAPI扩展和I
  • R包中的源文件

    我正在构建一个非常基本的 R 包供我自己使用 有些文件需要另一个文件中的函数 因此 R 文件尝试获取 R 文件 这在构建过程中失败 两者都不 source util R nor source util R 工作 R 找不到该文件 所有文件都
  • 无法在 Android Nougat 通知的小图标中使用黄色

    我在 Android 7 x 中将通知小图标设置为黄色时遇到问题 我在用着notification setColor Color YELLOW 在构建通知对象时 它显示橄榄色而不是黄色 也尝试过使用notification setColor
  • RxJava 的后备 Observable

    我正在寻找一种更好的方法来实现使用 RxJava 时针对空结果的简单 Observable 后备系统 这个想法是 如果对一组数据的本地查询导致零项 则应该进行回退查询 可能是网络调用或其他查询 目前 我的代码包含以下内容 Observabl
  • WCF XML 结构 - 如何删除包装器节点?

    我在使用 List 对象时遇到问题 DataContract public class Recipe DataMember Name Allergies public List
  • AttributeError:类型对象“MyUser”没有属性“USERNAME_FIELD”

    我正在 django 中构建一个自定义 User 类 用于创建注册应用程序 每次尝试 makemigrations 时 我都会收到上述错误 据我所知 我的代码是根据 django 文档here https docs djangoprojec
  • 对 socket.io 使用 http 和 https

    我正在努力使socket io两者都工作http and https连接 但根据我当前的配置 它似乎只能在其中之一上工作 使用以下配置选项 它可以通过以下方式访问我的应用程序https 但是当尝试通过访问它时http它无法连接并且我收到错误
  • 反转字符串中单词的顺序

    我正在准备初级工作面试 我试图反转字符串中单词的顺序 但我的输出是一堆毫无意义的垃圾 我认为问题可能是因为我在函数中使用 char 无论如何 这是我的代码 include
  • .NET 4.8 中的异步等待递归导致 StackoverflowException(.Net Core 3.1 中没有!)

    为什么下面的代码在 Net4 8中只有17深度递归会导致StackOverflowException 然而 这在 NetCore 3 1 中不会发生 我可以将计数设置为 10 000 它仍然有效 class Program static a