技术开发 频道

ASP.NET的各种异步操作实现详细解析

  基于事件模式的异步页

  如果您看过我的博客【C#客户端的异步操作】,那么对【基于事件模式的异步】这个词就不会再感到陌生了。在那篇博客中,我就对这种异步模式做过介绍,只不是,上次是在WinForm程序中演示的而已。为了方便对比,我再次把那段代码贴出来:

/// <summary>
/// 基于事件的异步模式
/// </summary>
/// <param name="str"></param>
private void CallViaEvent(string str)
{
    MyAysncClient
<string, string> client = new MyAysncClient<string, string>(ServiceUrl);
    client.OnCallCompleted
+= new MyAysncClient<string, string>.CallCompletedEventHandler(client_OnCallCompleted);
    client.CallAysnc(
str, str);
}

void client_OnCallCompleted(
object sender, MyAysncClient<string, string>.CallCompletedEventArgs e)
{
    
//bool flag = txtOutput.InvokeRequired;    // 注意:这里flag的值是false,也就是说可以直接操作UI界面
    
if( e.Error == null )
        ShowResult(
string.Format("{0} => {1}", e.UserState, e.Result));
    
else
        ShowResult(
string.Format("{0} => Error: {1}", e.UserState, e.Error.Message));        
}

   上次,我就解释过,这种方法在WinForm中非常方便。幸运的是,ASP.NET的异步页也支持这种方式。

  ASP.NET的异步页中的实现代码如下:

private void CallViaEvent(string str)
{
    MyAysncClient
<string, string> client = new MyAysncClient<string, string>(ServiceUrl);
    client.OnCallCompleted
+= new MyAysncClient<string, string>.CallCompletedEventHandler(client_OnCallCompleted);
    client.CallAysnc(
str, str);
}

void client_OnCallCompleted(
object sender, MyAysncClient<string, string>.CallCompletedEventArgs e)
{
    Trace.Warn(
"client_OnCallCompleted ThreadId = " + System.Threading.Thread.CurrentThread.ManagedThreadId.ToString());

    
if( e.Error == null )
        labMessage.Text
= string.Format("{0} => {1}", e.UserState, e.Result);
    
else
        labMessage.Text
= string.Format("{0} => Error: {1}", e.UserState, e.Error.Message);
}

 

  搞什么呀,这二段代码是一样的嘛。 您是不是也有这样的感觉呢?

  仔细看这二段代码,还是能发现它们有区别的。这里我就不指出它们了。它们与异步无关,说出它们意义不大,反而,我更希望您对【基于事件模式的异步】留个好印象:它们就是一样的

  再来看一下如何发出多个异步任务:

protected void button1_click(object sender, EventArgs e)
{
    Trace.Write(
"button1_click ThreadId = " + System.Threading.Thread.CurrentThread.ManagedThreadId.ToString());
    
string str = textbox1.Text;

    
// 注意:这个异步任务,我设置了2秒的超时。它应该是不能按时完成任务的。
    MyAysncClient
<string, string> client = new MyAysncClient<string, string>(ServiceUrl, 2000);
    client.OnCallCompleted
+= new MyAysncClient<string, string>.CallCompletedEventHandler(client_OnCallCompleted);
    client.CallAysnc(
str, str);        // 开始第一个异步任务
    

    
string str2 = "T2_" + Guid.NewGuid().ToString();
    MyAysncClient
<string, string> client2 = new MyAysncClient<string, string>(ServiceUrl);
    client2.OnCallCompleted
+= new MyAysncClient<string, string>.CallCompletedEventHandler(client2_OnCallCompleted);
    client2.CallAysnc(str2, str2);        
// 开始第二个异步任务
}    

void client2_OnCallCompleted(
object sender, MyAysncClient<string, string>.CallCompletedEventArgs e)
{
    ShowCallResult(
2, e);


    
// 再来一个异步调用
    
string str3 = "T3_" + Guid.NewGuid().ToString();
    MyAysncClient
<string, string> client3 = new MyAysncClient<string, string>(ServiceUrl);
    client3.OnCallCompleted
+= new MyAysncClient<string, string>.CallCompletedEventHandler(client3_OnCallCompleted);
    client3.CallAysnc(str3, str3);        
// 开始第三个异步任务
}

   页面的执行过程如下图:

  这里要说明一下了:在【C#客户端的异步操作】中我就给出这个类的实现代码,不过,这次我给它增加了超时功能,增加了一个重载的构造函数,需要在构造函数的第二个参数传入。今天我就不贴出那个类的代码了,有兴趣的自己去下载代码阅读吧。在上次贴的代码,你应该可以发现,在CallAysnc()时,就已经开始了异步操作。对于本示例来说,也就是在button1_click就已经开始了二个异步操作。

  这是个什么意思呢?

  可以这样来理解:前二个任务显然是和LoadComplete,PreRender事件阶段的代码在并行执行的。

  有意思的是:第三个任务是在第二个任务的结束事件中开始的,但三个任务的结束操作全在页面的PreRender事件才得到处理。下面我再把这个例子来改一下,就更有趣了:

protected void button1_click(object sender, EventArgs e)
{
    Trace.Write(
"button1_click ThreadId = " + System.Threading.Thread.CurrentThread.ManagedThreadId.ToString());
    
string str = textbox1.Text;

    
// 注意:这个异步任务,我设置了2秒的超时。它应该是不能按时完成任务的。
    MyAysncClient
<string, string> client = new MyAysncClient<string, string>(ServiceUrl, 2000);
    client.OnCallCompleted
+= new MyAysncClient<string, string>.CallCompletedEventHandler(client_OnCallCompleted);
    client.CallAysnc(
str, str);        // 开始第一个异步任务

    System.Threading.Thread.Sleep(
3000);

    
string str2 = "T2_" + Guid.NewGuid().ToString();
    MyAysncClient
<string, string> client2 = new MyAysncClient<string, string>(ServiceUrl);
    client2.OnCallCompleted
+= new MyAysncClient<string, string>.CallCompletedEventHandler(client2_OnCallCompleted);
    client2.CallAysnc(str2, str2);        
// 开始第二个异步任务
}    

   现在,在第一个任务发出后,我让线程等待了3秒,也就是等到了第一个任务的超时。然后再开始第二个任务。

  也就是说:在button1_click事件还没执行完毕,第一个任务就结束了。

  现在,您可以猜一下,此时的执行过程是个什么样的。

  猜好了就来看下图吧。

  现在明白了吧:哪怕是在PostBackEvent阶段就结束的任务,也要等到PreRender之后才能得到处理。

  至于为什么会是这样的,我以后再讲。今天只要记住本文的第一张图片就好了。

  我可是好不容易才找出这张图片来的,且为了让您能看得更清楚,还花了些时间修改了它。

  在那个图片后面我还说过:在一个异步页的【页面生命周期】中,所有异步任务在执行时所处的阶段。 并在后面注明了这里的所有这个词也不太恰当。现在可以解释为什么不恰当了:

  【基于事件模式的异步】的开始阶段并不一定要PreRender事件之后,而对于前二种异步面的实现方式则是肯定在PreRender事件之后。

  至于这其中的原因,同样,您要等待我的后续博客了。

0
相关文章