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

时间:2019-04-21 08:10:47

标签: c# .net async-await

查看以下代码:

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

假设a,b,c,d也嵌套了异步等待(依此类推)

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

问题(理论上):

由于每个状态机都保存在内存中,这会导致大量内存消耗吗?
问这个问题可能很模糊,但是如果有很多状态,似乎不可避免地要问保持状态机的大小。

3 个答案:

答案 0 :(得分:8)

  

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

极不可能。每个状态机将在外部占用几十个字节。

因此,只有当您有很多时,才有意义。嵌套并不会真正导致这种情况,但是执行Task[]的成员可能会。

但这并不是真正的新事物,也不是与其他任何资源类型不同的形式。

答案 1 :(得分:6)

  

异步/等待POV-每次等待,都会保留一个状态机。

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

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

SharpLab是探索此类材料的绝佳资源。 Example

答案 2 :(得分:3)

有一个额外的费用,但相对较轻。

与常规功能相比的额外费用:

  • 状态机的类
  • 此类的实例
  • 执行阶段的一个整数
  • AsyncTaskMethodBuilder实例

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

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

也有一些在线工具可以做到这一点(例如Sharplab.io) 参见results of decompilation of a trivial async function