异步 - 等待调用链中的分配

时间:2018-05-18 13:14:25

标签: c# .net-core task

我试图更好地理解由于编译器在C#中为async-await生成代码而发生的堆分配。

请考虑以下代码:

Array
(
    [0] => Array
        (
            [0] => 08004
            [SQLSTATE] => 08004
            [1] => 911
            [code] => 911
            [2] => [Microsoft][ODBC Driver 13 for SQL Server][SQL Server]Database 'SAMEORIGIN' does not exist. Make sure that the name is entered correctly.
            [message] => [Microsoft][ODBC Driver 13 for SQL Server][SQL Server]Database 'SAMEORIGIN' does not exist. Make sure that the name is entered correctly.
        )

    [1] => Array
        (
            [0] => 42000
            [SQLSTATE] => 42000
            [1] => 102
            [code] => 102
            [2] => [Microsoft][ODBC Driver 13 for SQL Server][SQL Server]Incorrect syntax near 'ClickJacking'.
            [message] => [Microsoft][ODBC Driver 13 for SQL Server][SQL Server]Incorrect syntax near 'ClickJacking'.
        )

    [2] => Array
        (
            [0] => 42000
            [SQLSTATE] => 42000
            [1] => 4145
            [code] => 4145
            [2] => [Microsoft][ODBC Driver 13 for SQL Server][SQL Server]An expression of non-boolean type specified in a context where a condition is expected, near 'expect'.
            [message] => [Microsoft][ODBC Driver 13 for SQL Server][SQL Server]An expression of non-boolean type specified in a context where a condition is expected, near 'expect'.
        )

)

来自ILSpy From ILSpy

这里我们将有3个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"); } 结构类型(一个用于AsyncStateMachine,一个用于OneAsync,一个用于TwoAsync)由编译器生成。

您能否确认我的假设是否正确?

  • 调用ThreeAsync方法(反过来调用链直到OneAsync)会导致 3 ThreeAsync结构类型被置于

  • 如果我在AsyncStateMachine方法中没有使用HttpClient而只是从它返回ThreeAsync,则会有2 Task.CompletedTask个结构类型(一个用于{{1}和AsyncStateMachine)之一。在这种情况下,当整个调用链同步执行时,没有OneAsync结构类型的堆分配?

2 个答案:

答案 0 :(得分:4)

您可以使用三种状态机,因为有三种异步方法。编译器始终创建的存根创建状态机。如果您将ThreeAsync更改为没有async修饰符,则可以使用两个,而只需编写一个返回已完成任务的常规方法。

你对堆分配是正确的:所涉及的等待者在检查时都已完成,因此不需要安排继续。这意味着除了(可能)任务之外,不需要任何堆分配。当您返回普通Task并且任务总是成功时,我也希望也可以使用缓存的已完成任务。

答案 1 :(得分:2)

基本上:两者都是;生成状态机,但不会在堆上结束,除非它实际上是异步的。在一些性能关键的场景中,大部分呼叫都是同步的,手动实现代码以在同步和异步之间切换可能是有利的:

static Task OneAsync()
{
    async Task Awaited(Task t)
    {
        await t;
        Console.WriteLine("OneAsync: End");
    }
    Console.WriteLine("OneAsync: Start");
    var task = TwoAsync();
    if (task.Status != TaskStatus.RanToCompletion)
        return Awaited(task);
    Console.WriteLine("OneAsync: End");
    return task; // could also have used Task.CompletedTask
}

请注意,这涉及一些手动复制 - 尤其是在结果之后或之后发生的事情(Console.WriteLine)。有一些方法可以减少这种情况,通常涉及更多的本地功能。另请注意,task.Status非常昂贵,并且在可用时(.NET Core或ValueTask<T>):IsCompletedSuccessfully应该是首选。