Ara*_*ami 517 c# task-parallel-library async-await asp.net-web-api
当你有服务器端代码(即一些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;
}
}
Run Code Online (Sandbox Code Playgroud)
Ste*_*ary 575
更新: ASP.NET Core没有SynchronizationContext
.如果您使用的是ASP.NET Core,则无论您是否使用它都无关紧要ConfigureAwait(false)
.
对于ASP.NET"Full"或"Classic"等等,本答案的其余部分仍然适用.
原帖(非核心ASP.NET):
ASP.NET团队的这段视频提供了有关async
在ASP.NET 上使用的最佳信息.
我已经读过它更高效,因为它不必将线程上下文切换回原始线程上下文.
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
介绍博客文章中有更多背景信息.
tug*_*erk 124
您的问题的简要回答:不.您不应该ConfigureAwait(false)
在应用程序级别调用.
TL;答案的DR版本:如果您正在编写一个您不了解您的消费者且不需要同步上下文的库(您不应该在我认为的库中),您应该始终使用ConfigureAwait(false)
.否则,您的库的使用者可能会以阻塞方式使用异步方法而面临死锁.这取决于具体情况.
这里有一个关于ConfigureAwait
方法重要性的更详细的解释(我的博客文章引用):
当您在等待具有await关键字的方法时,编译器会代表您生成大量代码.此操作的目的之一是处理与UI(或主)线程的同步.此功能的关键组件是
SynchronizationContext.Current
获取当前线程的同步上下文.SynchronizationContext.Current
根据您所处的环境填充.GetAwaiter
任务的方法可以查找SynchronizationContext.Current
.如果当前同步上下文不为null,则传递给该awaiter的延续将返回到该同步上下文.当使用阻塞方式使用新异步语言功能的方法时,如果您有可用的SynchronizationContext,则最终会出现死锁.当您以阻塞方式使用此类方法(等待Task with Wait方法或直接从Task的Result属性获取结果)时,您将同时阻止主线程.当最终任务在线程池中的该方法内完成时,它将调用延续以回发到主线程,因为它
SynchronizationContext.Current
是可用的并且被捕获.但这里有一个问题:UI线程被阻止,你有一个死锁!
另外,这里有两篇很棒的文章,完全适合您的问题:
最后,Lucian Wischik有一个关于这个主题的精彩视频:异步库方法应该考虑使用Task.ConfigureAwait(false).
希望这可以帮助.
Mic*_*ick 20
我在使用ConfigureAwait(false)时发现的最大缺点是线程文化被恢复为系统默认值.如果您已经配置了文化,例如......
<system.web>
<globalization culture="en-AU" uiCulture="en-AU" />
...
Run Code Online (Sandbox Code Playgroud)
并且您将托管在其文化设置为en-US的服务器上,然后您将在ConfigureAwait(false)之前找到CultureInfo.CurrentCulture将返回en-AU并在您获得en-US之后.即
// CultureInfo.CurrentCulture ~ {en-AU}
await xxxx.ConfigureAwait(false);
// CultureInfo.CurrentCulture ~ {en-US}
Run Code Online (Sandbox Code Playgroud)
如果您的应用程序正在执行任何需要特定于文化的数据格式化的操作,那么在使用ConfigureAwait(false)时您需要注意这一点.
Ali*_*tad 11
我对以下方面的实施有一些一般性的想法Task
:
using
.ConfigureAwait
是在4.5中引入的.Task
是在4.0中引入的.Task.ContinueWith
它们没有b/c它实现了上下文切换是昂贵的并且它默认关闭.我有一些关于这个主题的帖子,但除了Tugberk的好答案之外,我还有一个问题就是你应该把所有API变成异步并理想地流动上下文.由于您正在执行异步操作,因此您可以简单地使用continuation而不是等待,因此不会导致死锁,因为在库中没有等待并且您保持流动以便保留上下文(例如HttpContext).
问题是当库公开同步API但使用另一个异步API时 - 因此您需要在代码中使用Wait()
/ Result
.
归档时间: |
|
查看次数: |
184718 次 |
最近记录: |