异步CTP错误 - 任务永远无法完成

4
首先,我要提前道歉:我无法将以下错误隔离为简单的控制台应用程序。然而,在我的相对简单的ASP.NET Web Forms应用程序中,以下代码将导致当前线程无限期地阻塞:
public class MyModule : IHttpModule
{
    public void Dispose()
    {
    }

    public void Init(System.Web.HttpApplication context)
    {
        context.BeginRequest += this.Context_BeginRequest;
    }

    private void Context_BeginRequest(object sender, EventArgs e)
    {
        Sleep().Wait();
        var x = 2; // This line is never hit.
    }

    private async Task Sleep()
    {
        await TaskEx.Run(() => System.Threading.Thread.Sleep(1000));
    }
}

任务状态仅保持为'WaitingForActivation'。有人知道为什么会这样吗?

它在控制台 exe 中运行良好;它似乎与 asp.net 有关。 - Marc Gravell
1个回答

5

编辑:Stephen Cleary的评论提供了更多信息:

AspNetSynchronizationContext 是最奇怪的实现。它将Post视为同步而不是异步,并使用锁来逐个执行其委托AspNetSynchronizationContext 不需要回调到同一线程(但需要获取锁);在Wait上发生死锁是因为继续等待锁定(由事件处理程序中的线程持有)。


我猜测存在一个“同步上下文”,它强制使得继续运行在与事件处理程序相同的线程上。您的事件处理程序正在阻塞该线程,因此继续运行不会发生,这意味着事件处理程序永远不会解除阻塞。
尽管这只是猜测——这是我现在能想到的唯一有意义的事情。
要尝试解除此阻塞的一种选择是将您的“Sleep”方法更改为:
private async Task Sleep()
{
    await TaskEx.Run(() => System.Threading.Thread.Sleep(1000))
                .ConfigureAwait(continueOnCapturedContext: false);
}

那将允许继续在不同的上下文中完成。
我很惊讶有这样的同步上下文,但愿所有这些都只发生在线程池上。可能 BeginRequest 会被特别处理。

感谢您提供的宝贵见解。将上述的'ConfigureAwait'添加到该方法中,解决了示例问题。然而,真正的异步方法实际上是低级程序集的一部分,也被其他组件使用。您建议我是否应该以这种方式修改我所有的库任务(TaskEx.Runs),或者有没有更好的方法来修复调用代码的问题(比如在这种情况下的IHttpModule)? - Lawrence Wagerfield
@LawrenceWagerfield:老实说,真正出了什么问题并不清楚——每个位都有自己的意义,我猜想。显然,在真实的代码中,你不只是想睡觉……如果你可以更多地说明你真正的代码试图做什么,我可能能够提供更多帮助。关于是否通常调用ConfigureAwait,这可能值得阅读Stephen Toub最近的文章:http://msdn.microsoft.com/en-us/magazine/hh456402.aspx - Jon Skeet
在这种情况下,底层库代码相当简单;它只是引发一个事件,由两个与数据库交互的操作处理。问题明显是隔离在IHttpModule中的;当从其他区域调用代码时,代码可以正常工作。是否有一种方法可以覆盖IHttpModule使用的同步上下文? - Lawrence Wagerfield
AspNetSynchronizationContext 是最奇怪的实现之一。它将 Post 视为同步而不是异步操作,并使用锁来逐个执行其委托。AspNetSynchronizationContext 不需要将线程调度回到相同的线程(但需要获取锁);在 Wait 上发生死锁是因为继续等待锁(由事件处理程序中的线程持有)。 - Stephen Cleary
@StephenCleary:啊,谢谢 - 这一切都有点更加清晰了,以一种奇妙而又怪异的方式 :) 我会将其编辑到答案中。 - Jon Skeet
显示剩余7条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接