ASP.NET MVC 中异步编程的最佳实践是什么?

What is the best practice for asynchronous programming in ASP.NET MVC?

根据 this 文章 ASP.NET 要求在控制器中使用相同的 SynchronizationContext 进行异步操作,否则它会阻塞 运行 线程。作为结论,作者提到我们应该使用这两种方法作为防止线程死锁的最佳实践:

  1. In your “library” async methods, use ConfigureAwait(false) wherever possible.
  2. Don’t block on Tasks; use async all the way down.

    Note: It is best to apply both best practices. Either one will prevent the deadlock, but both must be applied to achieve maximum performance and responsiveness.

但是是什么目的导致 Microsoft 使用相同的 SynchronizationContext?可能正在使用禁用相同同步上下文限制的 ConfigureAwait(false),可能会导致例如不可预测的行为或性能问题。因此,尽可能使用 ConfigureAwait(false) 真的是好习惯吗?

ASP.NET requires using the same SynchronizationContext for asynchronous operations in the controller, otherwise it blocks the running thread.

我不太确定那句话是什么意思。 ASP.NET 不需要 "the same" 同步上下文。为了让您进入 HttpContext,您需要 SynchronizationContext.

But what purpose led Microsoft to use the same SynchronizationContext?

我建议您阅读 It's All About SynchronizationContext 以更全面地了解同步上下文。基本上,它是一种允许您在特定线程上 post 延续的机制。例如,它可用于将工作编组回 UI 应用程序内的 UI 消息循环线程。

May be using of the ConfigureAwait(false), which disables the same sync context restriction, might lead to e.g. unpredictable behavior or performance issues

恰恰相反。将工作编组回同步上下文确实有(非常小的)开销,因为请求需要 posted(使用抽象 SynchronizationContext.Post)回到所需的线程和上下文。通过使用 ConfigureAwait(false),您 节省了 时间,并且只需继续执行分配的线程。

Thus, is it really good practice to use the ConfigureAwait(false) wherever is possible?

这样做的主要原因是为了避免死锁。当有人调用 Task.ResultTask.Wait 而不是使用 await 异步等待时,您会遇到经典的死锁场景,其中同步上下文试图 post 继续返回线程,但它目前被阻止,因为 ResultWait 正在阻止调用。另一个原因是您获得的性能开销很小。

编辑:

Why doesn't Microsoft encapsulate this method inside the Task class? It looks like the ConfigureAwait(false) has just only the pluses. Is there any minuses?

让我们设想以下场景:您执行一个 return 是 Task 并等待使用 await 的方法,紧接着您更新一些 UI 元素.现在,什么对你来说更自然?与之前的 "environment" 相比,您更愿意隐式 return(UI 线程),还是您更愿意必须明确指定您想要 return到同样的环境?

我认为前者"feels"对用户来说更自然。

示例:

public Task FetchAndReturnAsync(string url)
{
    var httpClient = new HttpClient();
    return httpClient.GetAsync(url);
}

你这样称呼它:

var awesomeUiResult = await FetchAndReturnAsync("http://www.google.com");
textBox.Text = awesomeUiResult;

你认为应该发生什么?您是能够在等待之后更新文本框,还是因为您现在不在原始上下文中而失败更自然?

ASP.NET requires using the same SynchronizationContext for asynchronous operations in the controller, otherwise it blocks the running thread.

有点,但不完全是。 ASP.NET 为每个传入请求创建一个 SynchronizationContext,并且此上下文根本不绑定到特定线程。但是,一次只有一个线程可以进入上下文,因此如果第二个线程试图在一个线程已经在其中时进入它,那么它将阻塞该线程。

what purpose led Microsoft to use the same SynchronizationContext?

ASP.NET 的 SynchronizationContext 管理每个请求的数据,例如 HttpContext.Current、文化和用户身份。

is it really good practice to use the ConfigureAwait(false) wherever is possible?

一般来说,是的,尤其是对于通用库。在 ASP.NET 上,ConfigureAwait(false) 不会给您带来太大好处 - 在某些情况下,性能会略有提高。它 可以 用于避免我在文章中提到的死锁,但最好(尤其是在 ASP.NET 上)一直使用 async