在ASP.NET中调用异步方法时混淆行为

时间:2012-10-03 04:24:09

标签: asp.net task-parallel-library async-await

我使用Visual Studio 2012创建了一个ASP WebApplication。

如果我按如下方式修改默认页面:

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()时,它会无限期挂起。

<小时/> 有趣的是,如果我在web.config中设置它:

<appSettings>

    <add key="aspnet:UseTaskFriendlySynchronizationContext" value="false" />
</appSettings>

然后它确实有效。 Wait函数等待休眠在另一个线程上完成,然后继续。


有人可以解释一下:

  • 为什么要挂?
  • 为什么他们有TaskFriendlySynchronizationContext的东西? (鉴于它导致任务挂起,我不会称之为“友好”)

  • 是否有从页面处理程序方法调用async方法的“最佳实践”?

这是我提出的一个可以实现的实现,但感觉就像笨拙的代码:

    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));
    }

1 个答案:

答案 0 :(得分:4)

  

为什么要挂?

代表Task的{​​{1}}正在尝试在其PerformSleepingTask之后恢复,以便await可以返回。它正在尝试重新进入ASP.NET请求上下文,该请求上下文被PerformSleepingTask的调用阻止。 This causes a deadlock, as I expound on my blog

要避免死锁,请遵循以下最佳做法:

  1. 一直使用Wait。请勿阻止async代码。
  2. 在“库”方法中使用async
  3.   

    为什么他们有ConfigureAwait(false)的东西? (鉴于它导致任务挂起,我不会称之为“友好”)

    TaskFriendlySynchronizationContext使用新的TaskFriendlySynchronizationContext(.NET 4.0 AspNetSynchronizationContext已重命名为TaskFriendlySynchronizationContext),它还使用了新的LegacyTaskFriendlySynchronizationContext - 知晓管道

    我不是百分之百确定这个,但我怀疑原因async适用于旧版SyncCtx的原因是旧的管道没有将SyncCtx放到位然而。我不确定为什么会以这种方式行事(除非Page_LoadPage.Async)。

      

    是否有从页面处理程序方法调用异步方法的“最佳实践”?

    Absolutely。您可以只创建事件处理程序false,也可以使用async void。第一种方法更容易,但第二种方法受到ASP.NET团队的青睐。