根据this文章,ASP.NET要求在控制器中使用相同的SynchronizationContext
进行异步操作,否则会阻塞正在运行的线程。作为结论,作者提到我们应该使用这两种方法作为防止线程死锁的最佳实践:
- 在“库”异步方法中,尽可能使用ConfigureAwait(false)。
- 醇>
不要阻止任务;一直使用async。
注意:最好同时应用这两种最佳做法。任何一方都会阻止 死锁,但两者都必须用于实现最大的性能和响应能力。
但是什么目的导致微软使用相同的SynchronizationContext
?可能正在使用禁用相同同步上下文限制的ConfigureAwait(false)
,可能导致例如不可预测的行为或性能问题。因此,在任何可能的地方使用ConfigureAwait(false)
是否真的很好?
答案 0 :(得分:3)
ASP.NET要求使用相同的SynchronizationContext 控制器中的异步操作,否则会阻塞 正在运行的线程。
我不确定那句话是什么意思。 ASP.NET不需要“相同”的同步上下文。为了让您进入HttpContext
,您需要SynchronizationContext
。
但是什么目的导致微软使用相同的
SynchronizationContext
?
我建议您阅读It's All About SynchronizationContext以了解有关同步上下文的更多信息。基本上,它是一种允许您在特定线程上发布延续的机制。例如,它可以用于将工作编组回UI应用程序内的UI消息循环线程。
可能正在使用ConfigureAwait(false),它会禁用相同的配置 同步上下文限制,可能导致例如不可预知的行为或 绩效问题
恰恰相反。编组工作回到同步上下文确实有(非常小的)开销,因为需要将请求(使用抽象SynchronizationContext.Post
)发布回所需的线程和上下文。通过使用ConfigureAwait(false)
,您保存该时间,并继续执行已分配线程的任务。
因此,使用ConfigureAwait(false)是否真的很好 哪里可能?
这样做的主要原因是避免死锁。当有人调用Task.Result
或Task.Wait
而不是使用await
异步等待时,您会得到经典的死锁情况,其中同步上下文试图将延续回发到线程上,但它当前是阻止,因为Result
和Wait
阻止了通话。另一个原因是您获得的性能开销很小。
为什么Microsoft不将此方法封装在Task类中? 看起来ConfigureAwait(false)只有加号。是 有什么缺点吗?
让我们想象以下场景:执行一个返回Task
的方法,并等待使用await
,然后更新一些UI元素。现在,对你来说更自然的是什么?您是否希望隐式返回到之前的相同“环境”(UI线程),或者您是否希望必须明确指定要返回到相同的环境?
我认为前者对用户来说“感觉”更自然。
示例:
public Task FetchAndReturnAsync(string url)
{
var httpClient = new HttpClient();
return httpClient.GetAsync(url);
}
你这样称呼它:
var awesomeUiResult = await FetchAndReturnAsync("http://www.google.com");
textBox.Text = awesomeUiResult;
您认为应该怎么做?您是否更自然能够在等待之后更新文本框,或者因为您现在不在原始上下文中而失败?
答案 1 :(得分:2)
ASP.NET要求在控制器中使用相同的SynchronizationContext进行异步操作,否则会阻止正在运行的线程。
排序,但不完全。 ASP.NET为每个传入请求创建一个SynchronizationContext
,并且此上下文根本不依赖于特定线程。但是,一次只有一个线程可以进入上下文,所以如果第二个线程尝试输入它而其中一个已经在其中,那么它将阻止该线程。
是什么目的导致Microsoft使用相同的SynchronizationContext?
ASP.NET的SynchronizationContext
管理每个请求数据,例如HttpContext.Current
,文化和用户身份。
在任何可能的地方使用ConfigureAwait(false)是否真的很好?
作为一般规则,是的,特别是对于通用库。在ASP.NET上,ConfigureAwait(false)
对您没有多大帮助 - 在某些情况下,性能会有轻微提升。 可以用于避免我在文章中提到的死锁,但是一直使用async
会更好(特别是在ASP.NET上)。