首先是一个直截了当的道歉:我无法将以下bug隔离到一个简单的控制台应用程序中。但是,在我相对简单的ASP.NET Web窗体应用程序中,以下代码将导致当前线程无限期阻塞:
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”。有人知道为什么会这样吗?
最佳答案
编辑:斯蒂芬·克利里的评论透露了更多的信息:AspNetSynchronizationContext
是最奇怪的实现。它treats Post as synchronous rather than asynchronous和uses a lock to execute its delegates one at a time。AspNetSynchronizationContext
不需要封送回同一线程(但需要获取锁);在Wait
上出现死锁是因为continuation正在等待锁(由事件处理程序中的线程持有)
我的猜测是有一个SynchronizationContext
强制continuation在与事件处理程序相同的线程上运行。您的事件处理程序正在阻止该线程,因此continuation永远不会运行,这意味着事件处理程序永远不会取消阻止。
不过,这只是一个猜测——这是我目前唯一能想到的有意义的事情。
尝试解除阻止的一个选项是将Sleep
方法更改为:
private async Task Sleep()
{
await TaskEx.Run(() => System.Threading.Thread.Sleep(1000))
.ConfigureAwait(continueOnCapturedContext: false);
}
这将允许在不同的上下文中完成延续。
我很惊讶有这么一个同步上下文,请注意…我希望所有这些都发生在线程池中。可能
BeginRequest
被轻微特殊处理。