在ASP.NET中调用异步方法时的行为混乱 [英] Confusing behaviour when invoking async methods inside ASP.NET
问题描述
我使用Visual Studio 2012创建了一个ASP WebApplication.
I created an ASP WebApplication using Visual Studio 2012.
如果我如下修改默认页面:
If I modify the default page as follows:
public partial class _Default : Page
{
static async Task PerformSleepingTask()
{
Action action = () =>
{
Thread.Sleep(TimeSpan.FromSeconds(0.5));
int dummy = 3; // Just a nice place to put a break point
};
await Task.Run(action);
}
protected void Page_Load(object sender, EventArgs e)
{
Task performSleepingTask = PerformSleepingTask();
performSleepingTask.Wait();
}
}
在呼叫performSleepingTask.Wait()
时,它会无限期挂起.
On the call to performSleepingTask.Wait()
it hangs indefinitely.
有趣的是,如果我在web.config中进行设置:
Interestingly, if I set this in the web.config:
<appSettings>
<add key="aspnet:UseTaskFriendlySynchronizationContext" value="false" />
</appSettings>
然后它可以工作. Wait
函数等待休眠在其他线程上完成,然后继续.
Then it does work. The Wait
function waits for the sleeping to finish on a different thread, then continues.
有人可以解释:
- 为什么挂起?
- 为什么他们有一个叫
TaskFriendlySynchronizationContext
的东西? (鉴于它导致任务挂起,我不会称其为友好的")
- Why does it hang?
- Why do they have something called
TaskFriendlySynchronizationContext
? (Given that it causes tasks to hang, I wouldn't call it "friendly")
- 是否存在从页面处理程序方法中调用
async
方法的最佳实践"?
- Is there a "best practice" for invoking
async
methods from page handler methods?
这是我想出的一个可行的实现,但是感觉像笨拙的代码:
Here's an implementation I came up with which works, but it feels like clumsy code:
protected void Page_Load(object sender, EventArgs e)
{
ManualResetEvent mre = new ManualResetEvent(false);
Action act = () =>
{
Task performSleepingTask = PerformSleepingTask();
performSleepingTask.Wait();
mre.Set();
};
act.BeginInvoke(null, null);
mre.WaitOne(TimeSpan.FromSeconds(1.0));
}
推荐答案
为什么挂起?
Why does it hang?
表示PerformSleepingTask
的Task
在其await
之后尝试恢复,以便PerformSleepingTask
可以返回.它正在尝试重新输入ASP.NET请求上下文,该上下文被调用Wait
阻止. 这会导致僵局,正如我在博客中所阐述的那样.
The Task
representing PerformSleepingTask
is trying to resume after its await
so that PerformSleepingTask
can return. It is trying to re-enter the ASP.NET request context, which is blocked by the call to Wait
. This causes a deadlock, as I expound on my blog.
为避免僵局,请遵循以下最佳实践:
To avoid the deadlock, follow these best practices:
- 完全使用
async
.不要阻塞async
代码. - 在您的库"方法中使用
ConfigureAwait(false)
.
- Use
async
all the way down. Don't block onasync
code. - Use
ConfigureAwait(false)
in your "library" methods.
为什么他们有一个叫
TaskFriendlySynchronizationContext
的东西? (鉴于它导致任务挂起,我不会称其为友好的")
Why do they have something called
TaskFriendlySynchronizationContext
? (Given that it causes tasks to hang, I wouldn't call it "friendly")
TaskFriendlySynchronizationContext
使用新的AspNetSynchronizationContext
(.NET 4.0 TaskFriendlySynchronizationContext
已重命名为LegacyTaskFriendlySynchronizationContext
),并且还使用了一个新的可识别async
的管道.
TaskFriendlySynchronizationContext
uses the new AspNetSynchronizationContext
(the .NET 4.0 TaskFriendlySynchronizationContext
has been renamed LegacyTaskFriendlySynchronizationContext
), and it also uses a new, async
-aware pipeline.
我不确定是否对此有100%的把握,但我怀疑认为Page_Load
适用于旧版SyncCtx的原因是旧管道尚未将SyncCtx放置到位.我不知道为什么会这样操作(除非Page.Async
是false
).
I'm not 100% sure about this one, but I suspect that the reason Page_Load
works for the legacy SyncCtx is that the old pipeline wasn't putting the SyncCtx in place yet. I'm not sure why it would act this way, though (unless Page.Async
was false
).
是否存在从页面处理程序方法中调用异步方法的最佳实践"?
Is there a "best practice" for invoking async methods from page handler methods?
绝对.您可以使事件处理程序为async void
,也可以使用RegisterAsyncTask(new PageAsyncTask(...));
.第一种方法比较容易,但是第二种方法受到ASP.NET团队的青睐.
Absolutely. You can either just make your event handlers async void
, or use RegisterAsyncTask(new PageAsyncTask(...));
. The first approach is easier but the second approach is favored by the ASP.NET team.
这篇关于在ASP.NET中调用异步方法时的行为混乱的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!