我正在研究ConfigureAwait的正确用法,我已经为ConfigureAwait确定了一个有效的用例(也就是说,在等待不需要调用线程同步上下文之后),并且看不到关于在异步方法上使用ConfigureAwait的任何建议。
我的代码如下所示
Public async Task StartMyProgram()
{
await RunBackgroundTask();
}
Private async Task RunBackgroundTask()
{
await Task.Delay(5000);
}要正确使用ConfigureAwait,我是否假设这两个await调用都应该使用,如下面的代码所示:
Public async Task StartMyProgram()
{
await RunBackgroundTask().ConfigureAwait(false);
}
Private async Task RunBackgroundTask()
{
await Task.Delay(5000).ConfigureAwait(false);
}还是我只需要在私有RunBackgroundTask方法上使用它?
发布于 2019-07-03 15:29:57
还是我只需要在私有RunBackgroundTask方法上使用它?
每个方法都应该自己做出ConfigureAwait(false)决策。这是因为每个方法都在await上捕获了自己的上下文,而不管调用/调用方法是做什么的。ConfigureAwait配置单个await;它根本不“流”。
因此,RunBackgroundTask需要确定“我需要继续讨论我的上下文吗?”如果不是,那么它应该使用ConfigureAwait(false)。
StartMyProgram需要确定“我需要继续讨论我的上下文吗?”如果不是,那么它应该使用ConfigureAwait(false)。
发布于 2019-07-03 15:12:48
这是一种简化,但您可以假设ConfigureAwait(false)是一种微妙的方式来表示“嘿,您将要调用的东西不会占用当前的同步上下文”。
这里的关键字是current:同步上下文用于与异步状态机同步。异步方法被转换为任务,只有当所有方法都按照您的要求完成时,整个序列才必须返回。要执行这种同步,内部任务调度程序需要一个同步上下文。在编写库时,您不知道调用方在做什么,特别是,您现在知道可能正在运行异步方法的其他线程(例如,不同线程中的并发异步方法,或消息泵)。因此,您可以安全地调用ConfigureAwait(false),指示运行时不要借用(并捕获)调用者同步上下文,而是使用新的上下文。
你为什么要这么做?首先,因为在不确定的状态下借款是不太好的。但是更重要的是,为了避免死锁:实际上,在执行异步方法期间,默认情况下使用调用者捕获的上下文。这意味着您可能会陷入死锁和/或微妙的问题,因为运行任务所需的线程可能会被您的方法卡住,从而导致死锁。
默认情况下,当您使用异步/等待时,它将在启动请求的原始线程上恢复。然而,如果另一个长期运行的进程目前已经接管了该线程,您将被困在等待它完成。为了避免这个问题,您可以使用一个名为ConfigureAwait的方法,其中包含一个false参数。当您这样做时,这会告诉Task,它可以在任何可用的线程上恢复自身,而不是等待最初创建它的线程。这将加快响应速度,避免许多死锁。
对于ConfigureAwait(true) (默认的线程),当您继续处理另一个线程时,线程同步上下文就会丢失,从而丢失区域性和/或语言设置以及其他东西,比如HttpContext.Current (在.NET标准中会发生)。
根据经验,您应该始终在库代码中使用ConfigureAwait(false),当您是多线程时也应该在代码中使用。这是一个例子,因为默认行为可能不适合于大多数情况。
发布于 2019-07-03 14:52:46
进入RunBackgroundTask时,您不知道SynchronizationContext是什么。因此,您真的不需要捕获它,应该继续使用.ConfigureAwait(false)。
https://stackoverflow.com/questions/56872447
复制相似问题