我试图更好地理解由于编译器为 C# 中的异步等待生成代码而发生的堆分配。
考虑以下代码:
static async Task OneAsync()
{
Console.WriteLine("OneAsync: Start");
await TwoAsync();
Console.WriteLine("OneAsync: End");
}
static async Task TwoAsync()
{
Console.WriteLine("TwoAsync: Start");
await ThreeAsync();
Console.WriteLine("TwoAsync: End");
}
static async Task ThreeAsync()
{
Console.WriteLine("ThreeAsync: Start");
var c = new HttpClient();
var content = await c.GetStringAsync("http://google.com");
Console.WriteLine("Content:" + content.Substring(0, 10));
Console.WriteLine("ThreeAsync: End");
}
这里我们有 3 种 AsyncStateMachine
结构类型(一种用于 OneAsync
,一种用于 TwoAsync
,一种用于 ThreeAsync
) 由编译器生成。
能否请您确认我的假设是否正确?
调用
OneAsync
方法(依次调用链,直到ThreeAsync
),将导致 3AsyncStateMachine
struct types 要放在堆上?如果我没有在
ThreeAsync
方法中使用 HttpClient 而只是从它返回Task.CompletedTask
,那么会有 2 个AsyncStateMachine
结构类型(一种用于OneAsync
,一种用于TwoAsync
)。在这种情况下,不会为AsyncStateMachine
结构类型分配堆,因为整个调用链会同步执行吗?
最佳答案
无论哪种方式,您都会有三个状态机,因为存在三个异步方法。编译器创建的 stub 总是创建一个状态机。如果您将 ThreeAsync
更改为没有 async
修饰符,而是只编写一个返回已完成任务的常规 方法,您将有两个.
关于堆分配,您是对的:所涉及的等待者在检查时都已完成,因此不需要安排继续。这意味着除了(可能)任务之外,不需要堆分配任何东西。当您返回纯 Task
并且任务总是成功时,我希望也使用缓存的已完成任务。
关于c# - 调用链中的异步等待分配,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/50412177/