2

我使用 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));
    }
4

1 回答 1

4

为什么会挂?

Task表示在其PerformSleepingTask之后试图恢复await以便PerformSleepingTask可以返回。它正在尝试重新进入 ASP.NET 请求上下文,该上下文被对Wait. 正如我在博客中所阐述的那样,这会导致僵局

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

  1. 一直使用async下去。不要阻塞async代码。
  2. ConfigureAwait(false)在您的“库”方法中使用。

为什么他们有一个东西叫TaskFriendlySynchronizationContext?(鉴于它会导致任务挂起,我不会称其为“友好”)

TaskFriendlySynchronizationContext使用新AspNetSynchronizationContext的(.NET 4.0TaskFriendlySynchronizationContext已重命名LegacyTaskFriendlySynchronizationContext),它还使用新的 -async感知管道。

我不是 100% 确定这个,但我怀疑旧版 SyncCtx的原因Page_Load是旧管道还没有放置 SyncCtx。不过,我不确定它为什么会这样(除非Page.Asyncfalse)。

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

绝对的。您可以只制作事件处理程序async void,也可以使用RegisterAsyncTask(new PageAsyncTask(...));. 第一种方法更容易,但第二种方法受到 ASP.NET 团队的青睐。

于 2012-10-03T10:52:18.190 回答