"async Task then await Task" 与 "Task then return task"

107
为了对异步编程和await有一些扎实的基础理解,我想知道在多线程、执行顺序和时间方面,这两个代码片段之间的区别是什么:

This:

public Task CloseApp()
{
        return Task.Run(
                         ()=>{ 
                                // save database
                                // turn off some lights
                                // shutdown application
                          });
}

相对于这个:

public async Task CloseApp()
{
        await Task.Run(
                         ()=>{ 
                                // save database
                                // turn off some lights
                                // shutdown application
                          });
}

如果我在这个程序中调用它:

private async void closeButtonTask()
{
    // Some Task 1
    // ..

    await CloseApp();
    
    // Some Task 2
    // ..
}

3
它们在异常包装方面有微妙的差别。 - SLaks
可能是重复问题 https://dev59.com/0WEi5IYBdhLWcg3wsODc - DavidG
1
closeButtonTask 应该返回 Task 而不是 void。 - michal.jakubeczy
1
我整理的这个sharplab示例展示了底层机制。 - Drew Noakes
2个回答

94

两种方式几乎相同(在线程等方面)。但是对于使用await的第二种方法,编译器将创建更多的开销。

声明为async且使用await的方法被编译器转换为状态机。因此,当您遇到await时,控制流会返回到调用方法,并在等待的Task完成后恢复执行async方法。

由于在await之后没有更多代码,因此无论如何都不需要使用await。只需返回Task即可。


3
当你使用"await"关键字时,控制流可能会返回到调用该方法的地方。特别地,当被等待的任务已经完成时,控制流将不会返回。 - acelent
@acelent 感谢您提供的详细信息,我从未考虑过任务已经完成时会发生什么。我不知道编译器逐行执行的确切细节。 - René Vogt
1
我完全不理解这个。 - monstro
1
当您的任务完成时,线程池中的一个线程将调用Post方法并通知调用者任务已完成。据我记得,异步方法的其余部分将作为ContinueWith执行。执行将从等待点继续,但可能在不同的线程中执行。 - Joseph Katzman
这篇博客文章讲解得非常清楚:https://devblogs.microsoft.com/premier-developer/dissecting-the-async-methods-in-c/简而言之,有一些优化逻辑可以防止不必要的等待。 - Daniel Dror

14

这两种方法之间的差异很少,基本上它们共享相同的语义。然而,在使用async/await版本时,会将内部任务的执行封装在一个外部编译器生成的任务中。非async版本则不会如此。因此,非async版本略微更高效。


"非异步版本效率略高。" 这要看情况,如果该方法被频繁调用,则使用async/await版本可能会产生明显的垃圾:状态机和另一个任务。 - acelent
@acelent:对我来说,生成一些0代垃圾仍然感觉相当边缘化。 - Falanwe
3
我想指出,并非所有人都是这样的情况(参见1),你不能依赖于任务或任务引用是那么短暂的事实(参见2于0:25:40于0:30:20)。 - acelent

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接