调用所有服务器的ConfigureAwait的最佳实践
当你有服务器端代码(例如一些ApiController
)和你的函数是异步的 - 所以它们返回Task<SomeObject>
- 当你等待函数调用ConfigureAwait(false)
,是否认为是最佳做法?
我读过它是更高性能的,因为它不必将线程上下文切换回原始线程上下文。 但是,对于ASP.NET Web Api,如果您的请求进入一个线程,并且您在等待某个函数并调用ConfigureAwait(false)
,当您返回ApiController
函数的最终结果时可能会将您置于不同的线程中。
我已经打出了一个我在下面讨论的例子:
public class CustomerController : ApiController
{
public async Task<Customer> Get(int id)
{
// you are on a particular thread here
var customer = await SomeAsyncFunctionThatGetsCustomer(id).ConfigureAwait(false);
// now you are on a different thread! will that cause problems?
return customer;
}
}
更新: ASP.NET Core没有SynchronizationContext
。 如果您使用的是ASP.NET Core,则无论使用ConfigureAwait(false)
还是不使用。
对于ASP.NET“完整”或“经典”或其他,这个答案的其余部分仍然适用。
原帖(适用于非核心ASP.NET):
由ASP.NET团队制作的视频提供了有关在ASP.NET上使用async
的最佳信息。
我读过它是更高性能的,因为它不必将线程上下文切换回原始线程上下文。
在UI应用程序中,只有一个UI线程需要“同步”回。
在ASP.NET中,情况有点复杂。 当一个async
方法恢复执行时,它从ASP.NET线程池中抓取一个线程。 如果使用ConfigureAwait(false)
禁用上下文捕获,则线程会直接继续执行该方法。 如果您不禁用上下文捕获,那么线程将重新输入请求上下文,然后继续执行该方法。
因此, ConfigureAwait(false)
不会为您节省ASP.NET中的线程跳转; 它为您节省重新输入请求上下文的时间,但这通常非常快。 如果您尝试对请求进行少量并行处理,则ConfigureAwait(false)
会很有用,但真正适用于大多数情况下的TPL。
但是,对于ASP.NET Web Api,如果您的请求进入一个线程,并且您在等待某个函数并调用ConfigureAwait(false),当您返回ApiController函数的最终结果时可能会将您置于不同的线程中。
其实,只是在await
可以做到这一点。 一旦你的async
方法达到了await
,该方法就会被阻塞,但该线程将返回到线程池。 当方法准备好继续时,线程池中的任何线程都被抢走并用于恢复方法。
ConfigureAwait
在ASP.NET中唯一的区别是该线程在恢复方法时是否进入请求上下文。
在我的MSDN文章SynchronizationContext
和我的async
介绍博客帖子中,我有更多的背景信息。
简要回答您的问题:不可以。您不应该在应用程序级别调用ConfigureAwait(false)
。
TL;长版答案:如果你正在编写一个你不了解你的消费者的库,并且不需要一个同步上下文(你不应该在我认为的库中),你应该总是使用ConfigureAwait(false)
。 否则,通过以阻塞方式使用异步方法,库的使用者可能会面临死锁。 这取决于情况。
这里有一些关于ConfigureAwait
方法的重要性的更详细的解释(来自我博客文章的引用):
当你用await关键字等待一个方法时,编译器代表你产生一堆代码。 此操作的目的之一是处理与UI(或主)线程的同步。 此功能的关键组件是SynchronizationContext.Current
,它获取当前线程的同步上下文。 SynchronizationContext.Current
根据您所处的环境进行填充GetAwaiter
方法查找SynchronizationContext.Current
。 如果当前同步上下文不为空,则传递给该等待器的延续将被发回到该同步上下文。
当以阻塞的方式使用使用新的异步语言功能的方法时,如果您有可用的SynchronizationContext,则会导致死锁。 当您以阻塞的方式使用这些方法时(等待Task with Wait方法或直接从Task的Result属性中获取结果),您将同时阻塞主线程。 当最终任务在线程池中的该方法内完成时,它将调用继续以回发到主线程,因为SynchronizationContext.Current
可用并被捕获。 但是这里有个问题:UI线程被阻塞,你有一个死锁!
另外,这里有两篇很棒的文章,适合你的问题:
最后,Lucian Wischik提供了一个很棒的短视频,关于这个主题:异步库方法应该考虑使用Task.ConfigureAwait(false)。
希望这可以帮助。
我使用ConfigureAwait(false)发现的最大缺点是线程文化被还原为系统默认值。 如果你已经配置了一种文化,例如......
<system.web>
<globalization culture="en-AU" uiCulture="en-AU" />
...
并且您将托管在其文化设置为en-US的服务器上,那么您将在ConfigureAwait(false)之前找到名为CultureInfo.CurrentCulture将返回en-AU并在您获得en-US之后。 即
// CultureInfo.CurrentCulture ~ {en-AU}
await xxxx.ConfigureAwait(false);
// CultureInfo.CurrentCulture ~ {en-US}
如果您的应用程序正在执行任何需要文化特定格式的数据,那么在使用ConfigureAwait(false)时需要注意这一点。
链接地址: http://www.djcxy.com/p/20557.html上一篇: Best practice to call ConfigureAwait for all server
下一篇: Single controller with multiple GET methods in ASP.NET Web API