2013-02-07 9 views
5

我一直在WebForms中测试异步,我的问题不是关于如何做某件事,而是关于如何工作的东西,是否工作。这是我的测试代码:WebForms中的异步/等待 - 如何在页面生命周期结束之前继续运行?

protected override void OnPreRender(EventArgs e) 
{ 
    Response.Write("OnPreRender<Br>"); 
} 
protected override void OnPreRenderComplete(EventArgs e) 
{ 
    Response.Write("OnPreRenderComplete<Br>"); 
} 
protected async override void OnLoadComplete(EventArgs e) 
{ 
    Response.Write("OnLoadComplete<br>"); 
    var t1 = Task.Factory.StartNew(() => { 
     System.Threading.Thread.Sleep(2000); 
     return 1; 
    }); 

    //This actually does run: 
    Response.Write((await t1).ToString()); 
} 

所以我的任务暂停了一会然后写出结果。我的问题是 - 我不希望这工作,因为控制已从OnLoadComplete方法屈服 - 我希望页面实际上完成呈现并在我的任务返回之前返回给客户端。

实际的输出是:

OnLoadComplete 
OnPreRender 
1OnPreRenderComplete 

所以,很显然,该方法的onLoadComplete得到控制,这样的OnPreRender可以运行,然后控制返回的onLoadComplete。我的预期结果是,“1”永远不会打印,因为后续事件会触发,并且页面的线程将被杀死,或者在发送响应后发生后任务写入。我想,鉴于上述情况,即使我延迟10秒,结果也完全相同,这并不奇怪。

我假设在WebForm引擎中有一些接线,确保在页面生命周期的下一阶段进行之前完成任何等待。有谁知道这是怎么发生的?我害怕在需要在其他事件之前完成的方法中使用异步/等待,因为担心继续太迟了,但如果它在内部处理,那么我不会担心。

回答

11

对于ASP.NET,您应该只在.NET 4.5上使用async方法。我会解释为什么最后。

我有an article on SynchronizationContext,这有助于填补这如何在ASP.NET上工作的空白。首先,请注意很久以前ASP.NET支持异步操作(.NET 2.0 IIRC)。您可以通过几种不同的方式注册异步操作,但对于此描述我们将重点关注SynchronizationContext.OperationStarted

ASP.NET为每个请求创建一个SynchronizationContext,并且它知道在所有注册操作完成之前(通过调用SynchronizationContext.OperationCompleted)请求未完成。 Event-based asynchronous pattern components(如BackgroundWorker)会在启动并完成时自动通知SynchronizationContext

同样,async void方法(新的task-based asynchronous pattern)会在启动和完成时自动通知SynchronizationContext。因此,当您将OnLoadComplete替换为async void方法时,编译器将为您插入代码,该代码将在开始时调用OperationStarted,并在完成时插入OperationCompleted

到目前为止很好 - ASP.NET现在知道要保持请求活动状态,直到该请求的所有异步操作都已完成。即使没有线程处理请求,情况也是如此。

现在需要注意的是:ASP.NET 4.5之前的ASP.NET会在请求级别处理此问题。在ASP.NET 4.5中,生命周期管道变得更加智能,因此它会延迟页面生命周期,直到异步操作完成。对于旧的ASP.NET,“pre”处理程序将在该管道中的那一点开始,但直到后期才可能完成。新的ASP.NET将延迟页面执行的其余部分,以确保在处理生命周期之前完成async处理程序。

此外,ASP.NET 4.5将检测您是否使用了应该不存在的处理程序的async处理程序,并且会通知您该错误。

相关问题