C# 如何在异步方法上使用ConfigureWait

C# 如何在异步方法上使用ConfigureWait,c#,asynchronous,task,C#,Asynchronous,Task,我正在研究ConfigureAwait的正确用法我已经确定了ConfigureAwait的一个有效用例,即在等待之后,不需要调用线程同步上下文,也看不到关于在异步方法上使用ConfigureAwait的任何建议 我的代码如下所示 Public async Task StartMyProgram() { await RunBackgroundTask(); } Private async Task RunBackgroundTask() { await Task.Delay(

我正在研究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方法中使用它?

当输入RunBackgroundTask时,您不知道它是什么。因此,您真的不需要捕获它,应该继续使用.ConfigureAwaitfalse。

这是一种简化,但您可以假设ConfigureAwaitfalse是一种微妙的方式,表示您将要调用的内容不会捕获当前同步上下文

这里的关键字是current:同步上下文用于与异步状态机同步。您的异步方法将转换为任务,并且只有当所有方法都按照您的请求完成时,整个序列才必须返回n。 要执行这种同步,内部任务调度器需要同步上下文。编写库时,您不知道调用方在做什么,尤其是现在,您知道可能正在运行异步方法的其他线程,例如不同线程中的并发异步方法或消息泵。 因此,您可以安全地调用ConfigureWaitFalse,指示运行时不要借用和捕获调用方同步上下文,而是使用一个新上下文

你为什么要那样做?首先,因为借用非确定性状态的东西是不好的。但更重要的是,要避免死锁:事实上,在异步方法的执行过程中,默认情况下使用的是调用方捕获的上下文。这意味着您可能会陷入死锁和/或微妙的问题,因为运行任务所需的线程可能会被您的方法卡住,从而导致死锁

默认情况下,当您使用async/await时,它将在启动请求的原始线程上恢复。但是,如果另一个长时间运行的进程当前已经接管了该线程,您将被困在等待它完成。为了避免此问题,可以使用名为ConfigureAwait的方法和false参数。当您这样做时,这告诉任务可以在任何可用的线程上恢复自身,而不是等待最初创建它的线程。这将加快响应速度并避免许多死锁

如果默认为ConfigureWaitTrue,则当您在另一个线程上恢复时,线程同步上下文将丢失,从而丢失区域性和/或语言设置以及其他内容,如HttpContext。当前这种情况在.NET标准中发生

根据经验,您应该始终在库代码中使用ConfigureWaitFalse,当您使用多线程时,也应该在代码中使用ConfigureWaitFalse。这是一个例子,因为默认行为可能不适用于大多数情况

还是我只需要在私有RunBackgroundTask方法中使用它

每个方法都应该自己做出配置决策。这是因为无论调用方/被调用方方法做什么,每个方法都在等待时捕获自己的上下文。配置等待配置单个等待;它根本不流动

所以,RunBackgroundTask需要确定我是否需要在上下文中继续?如果否,则应使用ConfigureWaitFalse

StartMyProgramm需要确定我是否需要根据上下文继续?如果否,则应使用ConfigureWaitFalse