长异步等待链是否会导致大量内存消耗? (理论上)

问题描述 投票:5回答:3

看看这段代码:

public async Task<T> ConsumeAsync()
    {
          await a();
          await b();
          await c();
          await d();
          //..
    }

让我们说a,b,c,d也有嵌套异步等待(依此类推)

Async / await POV - 对于每个await,都有一台状态机。

问题(理论):

由于每个状态机都保存在内存中,这是否会导致大量内存消耗? 这可能是一个模糊的问题,但如果有很多州,似乎不可避免地想到保留国家机器的大小。

c# .net async-await
3个回答
6
投票

异步/等待POV - 对于每个等待,都有一个状态机被保留。

不对。编译器为每个async方法生成状态机。方法中的本地提升到状态机上的字段中。该方法的主体(基本上)被分解为switch语句,每个case对应于await语句之间的方法的一部分。 int用于跟踪方法的哪个位已被执行(即接下来应该执行哪个case)。

您的方法a()b()等可能有自己的状态机,或者它们可能没有(取决于它们是否标记为async)。即使他们这样做,在您的示例中,一次只会实例化其中一个状态机。

SharpLab是探索这些东西的绝佳资源。 Example


8
投票

由于每个状态机都保存在内存中,这是否会导致大量内存消耗?

非常不可能。每个状态机在外面占用几十个字节。

所以只有当你拥有很多这样的东西时才会有意义。嵌套并不会真正导致这种情况,但执行Task[]的成员可能会。

但这与任何其他资源类型并不是新的或不同的。


3
投票

需要额外付费,但相对较小。

与常规功能相比的额外成本:

  • 状态机的类
  • 这个类的实例
  • 执行阶段的一个int
  • AsyncTaskMethodBuilder实例

此外,函数的局部变量将转换为状态机的字段。这会将一些内存从堆栈移到堆中。

我建议反编译一些简单的异步函数,以查看生成的状态机并具有直观的期望。

还有一些在线工具可以做到这一点(如sharplab.io)请参阅results of decompilation of a trivial async function

© www.soinside.com 2019 - 2024. All rights reserved.