ASP.NET 2.0 异步页面原理浅析 [2]

来源:互联网 发布:oracle数据库管理试题 编辑:程序博客网 时间:2024/06/05 02:10

 

                                      

      可以看到 Page_Load 和 BeginAsyncOperation 方法都是在 ID 为 2816 的线程中被调用,其调用源也都是处理 HTTP 请求的 HttpRuntime.ProcessRequest 方法;而 EndAsyncOperation 则是在另外一个 ID 为 3376 的线程中调用,调用源也是完成网络读操作的 Connection.ReadComplete 方法。

      而从实现角度来看,AddOnPreRenderCompleteAsync 方法将异步页面处理的启动和停止方法,放到一个 Page.PageAsyncInfo 对象中。此对象维护了与页面相关的各种上下文信息,以及开始、停止和状态的数组。而 RegisterAsyncTask 方法也是类似,将 PageAsyncTask 实例放到 PageAsyncTaskManager 类型的管理器中。


 
 1class Page
 2{
 3    private Page.PageAsyncInfo _asyncInfo;
 4    private PageAsyncTaskManager _asyncTaskManager;
 5
 6  public void AddOnPreRenderCompleteAsync(BeginEventHandler beginHandler, EndEventHandler endHandler, object state)
 7  {
 8      // 处理参数和状态异常情况
 9      
10      // 延迟构造异步页面信息
11      if (_asyncInfo == null)      
12        _asyncInfo = new Page.PageAsyncInfo(this);  
13      
14      _asyncInfo.AddHandler(beginHandler, endHandler, state);
15    }

16    
17    public void RegisterAsyncTask(PageAsyncTask task)
18    {
19      // 处理参数和状态异常情况
20      
21      // 延迟构造异步任务管理器
22    if (this._asyncTaskManager == null)
23      _asyncTaskManager = new PageAsyncTaskManager(this);
24     
25    _asyncTaskManager.AddTask(task);
26    }

27}


      HttpApplication 在处理页面请求时,通过其 pipeline 的 CallHandlerExecutionStep 步骤,调用页面的 BeginProcessRequest 方法,其伪代码如下:
 
 1void HttpApplication.IExecutionStep.Execute()
 2{
 3  // 从上下文中获取获取当前页面的处理器
 4  HttpContext context = _application.Context;
 5  IHttpHandler handler = context.Handler;
 6    
 7  if (handler == null)
 8  {
 9    _sync = true;
10  }

11  else if (handler is IHttpAsyncHandler)
12  {
13    // 如果是异步处理器,则调用异步处理开始方法
14        IHttpAsyncHandler asyncHandler = (IHttpAsyncHandler) handler1;
15    
16    _sync = false;
17    _handler = asyncHandler;
18    
19    IAsyncResult result = asyncHandler.BeginProcessRequest(context, _completionCallback, null);
20            
21        // 如果的确是异步操作,就直接返回            
22        if (!result.CompletedSynchronously)
23            return;
24      
25    // 否则恢复同步的页面处理流程
26    _sync = true;
27    _handler = null;
28    asyncHandler.EndProcessRequest(result);
29  }

30  else
31  {
32      // 采用同步模式处理页面
33    _sync = true;
34    
35    _application.SyncContext.SetSyncCaller();
36    try
37    {
38      handler.ProcessRequest(context);
39    }

40    finally
41    {
42      _application.SyncContext.ResetSyncCaller();
43    }

44  }

45}


      而 ASP.NET 页面一旦通过 Page 标记定义为异步模式,其编译生成的 Page 子类就会实现 IHttpAsyncHandler 接口。
      例如对上述例子,我们可以通过 !ClrStack 命令看到页面被编译为名称为 ASP.asyncpage_aspx 的类型。

!clrstack
OS Thread Id: 0xb00 (2816)
ESP       EIP     
0361d9d4 05a9c817 AsyncPage.Page_Load(System.Object, System.EventArgs)
...
0361dd5c 0545b90e System.Web.UI.Page.AsyncPageBeginProcessRequest(System.Web.HttpContext, System.AsyncCallback, System.Object)
0361dd98 0545b813 ASP.asyncpage_aspx.BeginProcessRequest(System.Web.HttpContext, System.AsyncCallback, System.Object)
0361ddec 0545b6b6 System.Web.HttpApplication+CallHandlerExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute()
0361de28 05400c18 System.Web.HttpApplication.ExecuteStep(IExecutionStep, Boolean ByRef)
...


      进一步使用 !DumpDomain 命令可以找到其页面编译的临时文件,如

!DumpDomain 

...

Assembly: 0023ade0 [D:/WINDOWS/Microsoft.NET/Framework/v2.0.50727/Temporary ASP.NET Files/asyncpage/ba0a3865/3cd7d92/App_Web_n97gem4v.dll]
ClassLoader: 0023abc0
SecurityDescriptor: 00229ac0
  Module Name
05a502cc D:/WINDOWS/Microsoft.NET/Framework/v2.0.50727/Temporary ASP.NET Files/asyncpage/ba0a3865/3cd7d92/App_Web_n97gem4v.dll

...


      使用 IL 反汇编根据打开此文件,可以看到 AsyncPage.aspx 被编译为 asyncpage_aspx 类型,如下所示:
 
 1public class asyncpage_aspx : AsyncPage, IHttpAsyncHandler, IHttpHandler
 2{
 3    public virtual IAsyncResult BeginProcessRequest(HttpContext context, AsyncCallback cb, object data)
 4    {
 5    return base.AsyncPageBeginProcessRequest(context, cb, data);
 6    }

 7 
 8    public virtual void EndProcessRequest(IAsyncResult ar)
 9    {    
10    base.AsyncPageEndProcessRequest(ar);
11    }

12}

13
14public interface IHttpAsyncHandler : IHttpHandler
15{
16  // Methods
17  IAsyncResult BeginProcessRequest(HttpContext context, AsyncCallback cb, object extraData);
18  void EndProcessRequest(IAsyncResult result);
19}


      其中 AsyncPage 类型是后台实现代码编译生成的类型,asyncpage_aspx 则是 .aspx 页面编译生成。

      而在 Page.AsyncPageBeginProcessRequest 方法中,将首先处理上下文环境初始化,初始化异步执行信息,以及相应回调函数的执行;然后会调用 PageAsyncTaskManager.RegisterHandlersForPagePreRenderCompleteAsync 将异步任务管理器中所有的异步任务,封装后注册到 Page.PageAsyncInfo 对象中维护的异步调用信息中;最后调用      其 CallHandlers 方法完成对异步处理开始方法的调用。完整的伪代码如下:
 
 1class Page
 2{
 3  protected IAsyncResult AsyncPageBeginProcessRequest(HttpContext context, AsyncCallback callback, object extraData)
 4    {
 5        // 处理上下文环境初始化
 6        
 7        // 初始化异步执行信息
 8        _asyncInfo.AsyncResult = new HttpAsyncResult(callback, extraData);
 9    _asyncInfo.CallerIsBlocking = callback == null;
10
11        // 执行相应回调函数
12        
13        // 注册异步任务
14        if ((_asyncTaskManager != null&& !_asyncInfo.CallerIsBlocking)    
15      _asyncTaskManager.RegisterHandlersForPagePreRenderCompleteAsync();
16    
17    // 调用所有的异步处理开始方法
18        _asyncInfo.CallHandlers(true);
19    
20        return _asyncInfo.AsyncResult;    
21    }

22}


      而在 PageAsyncTaskManager 中被管理的异步任务,会作为一个异步执行信息注册到 PageAsyncInfo 中去。并在其被调用时,实际调用 PageAsyncTaskManager 类型的 ExecuteTasks 方法,实现较为复杂的异步调用逻辑。
 
 1internal class PageAsyncTaskManager
 2{
 3    internal void RegisterHandlersForPagePreRenderCompleteAsync()
 4    {
 5    _page.AddOnPreRenderCompleteAsync(new BeginEventHandler(this.BeginExecuteAsyncTasks), new EndEventHandler(this.EndExecuteAsyncTasks));
 6    }

 7    
 8    private IAsyncResult BeginExecuteAsyncTasks(object sender, EventArgs e, AsyncCallback cb, object extraData)
 9    {
10    return ExecuteTasks(cb, extraData);
11    }

12    
13    private void EndExecuteAsyncTasks(IAsyncResult ar)
14    {
15    _asyncResult.End();
16    }

17}



      以上我们对异步页面的目的、范围、使用方式和实现原理等,有了一个大致的了解。并针对异步任务的管理做了简要的分析,基本上已经能弄清异步页面的静态运行机制如何。下一节我们将从动态执行的角度,对两级异步任务,以及相应的调度和线程使用做进一步探索。