解析VS.Net线程异步调用(二)
广告投放★自助友情CMS落伍广告联盟晒乐广告联盟脉动广告联盟品味广告联盟
广告位可自定样式联系QQ:38355018个文字广告月20元广告联系QQ:3835501广告位可自定样式
8个文字广告月20元黄金广告位每月20元广告位可自定样式联系QQ:3835501广告位可自定样式
左旋肉碱、全国包邮
买二送一、无效退款

文章浏览→编程相关.Net编程→解析VS.Net线程异步调用(二)

解析VS.Net线程异步调用(二)
解析VS.Net线程异步调用(二)

 

使用.NET提供的异步调用服务


当你在Visual Studio中加一个Web引用(Reference)的时候,.NET在后台给你生成了一个代理类。令人惊讶的是异步调用的函数也自动被生成了。就前面给的Web Service而言,这个自动生成的代理类为。


//-----------------------------------------------------------------------
// <autogenerated>
//    This code was generated by a tool.
//    Runtime Version: 1.0.3705.209
//
//    Changes to this file may cause incorrect behavior and will be lost if
//    the code is regenerated.
// </autogenerated>
//-----------------------------------------------------------------------
//
// This source code was auto-generated by Microsoft.VSDesigner, Version
// 1.0.3705.209.
//
namespace AsyncClient_01.localhost {
   using System.Diagnostics;
   using System.Xml.Serialization;
   using System;
   using System.Web.Services.Protocols;
   using System.ComponentModel;
   using System.Web.Services;
  
   /// <remarks/>
   [System.Diagnostics.DebuggerStepThroughAttribute()]
   [System.ComponentModel.DesignerCategoryAttribute("code")]
   [System.Web.Services.WebServiceBindingAttribute(
Name="StockPriceSoap", Namespace="http://tempuri.org/")]
   public class StockPrice :
System.Web.Services.Protocols. SoapHttpClientProtocol
    
      /// <remarks/>
      public StockPrice() {
         this.Url = "http://localhost/StockService/StockService.asmx";
      }
     
      /// <remarks/>
      [System.Web.Services.Protocols.SoapDocumentMethodAttribute
("http://tempuri.org/getStockPrice",
RequestNamespace="http://tempuri.org/",
ResponseNamespace="http://tempuri.org/",
Use=System.Web.Services.Description.SoapBindingUse.Literal,
ParameterStyle=
System.Web.Services.Protocols.SoapParameterStyle.Wrapped)]

      public System.Double getStockPrice(string stockSymbol) {
         object[] results = this.Invoke("getStockPrice", new object[] {
                  stockSymbol});
         return ((System.Double)(results[0]));
      }
     
      /// <remarks/>

      public System.IAsyncResult BegingetStockPrice
(string stockSymbol, System.AsyncCallback callback, object asyncState)
{
         return this.BeginInvoke("getStockPrice", new object[] {
                  stockSymbol}, callback, asyncState);
      }
     
     
/// <remarks/>

      public System.Double EndgetStockPrice(System.IAsyncResult asyncResult)
{
         object[] results = this.EndInvoke(asyncResult);
         return ((System.Double)(results[0]));
      }
   }
}
   
 


程序中的黑体部分就是关于异步调用函数的程序段。你调用BegingetStockPrice()后将返回一个具有IAsyncResult界面的对象。通过它你就可以最终拿到函数的运行结果。Microsoft提供的异步调用比较灵活,下面就几种常见的用法做个介绍。

1.使用Pooling方法得到返回结果

你调用BegingetStockPrice()后得到一个具有IAsyncResult界面的对象。它提供了IsCompleted的属性。当这个值为"True"的时候,你就可以通过调用EndgetStockPrice()来拿到函数运行的结果。

更重要的是,你可以在异步调用的时候"放弃Abort"调用。下面的程序段是用3个"按钮(Button)"来示意如何使用异步调用,检查调用是否结束以及放弃调用。


//客户端的WebService引用
private localhost.StockPrice m_stockService;
private IAsyncResult m_handle; 
//异步调用WebMethod
private void button2_Click(object sender, System.EventArgs e)
{
    m_stockService = new localhost.StockPrice();
    m_handle = m_stockService.BegingetStockPrice("IBM",null,null);
    messageBox.Text += "Function is invoked." + System.Environment.NewLine;
 }
//检查异步调用是否完成。如果完成的话,就取出调用结果
private void button3_Click(object sender, System.EventArgs e)
{
    if(m_handle==null)
    {
       MessageBox.Show(this, "No function is called!");
       return;
    }
    if(m_handle.IsCompleted == false)
       messageBox.Text += "Price is not ready yet"  + System.Environment.NewLine;
    else
    {
       double price = m_stockService.EndgetStockPrice(m_handle);
       messageBox.Text += "The Price is: " + price + System.Environment.NewLine;
    }
}
//放弃异步调用
private void button4_Click(object sender, System.EventArgs e)
{
    if(m_handle!=null)
    {
       WebClientAsyncResult result = (WebClientAsyncResult)m_handle;
       result.Abort();
       m_handle = null;
    }
    messageBox.Text += "Function call is aborted!" + System.Environment.NewLine;
}

 


2.使用WaitHandle

你调用BegingetStockPrice()后得到一个具有IAsyncResult界面的对象。它提供了AsyncWaitHandle的属性。调用它的WaitOne()函数可以使程序被阻拦直到另外一个线程函数调用完成。之后程序将继续往下执行。


private void button8_Click(object sender, System.EventArgs e)
{
   if(this.m_stockService ==null)
m_stockService = new localhost.StockPrice();
   m_handle = m_stockService.BegingetStockPrice("IBM",null,null);
   messageBox.Text = "The function is called" + System.Environment.NewLine;
   m_handle.AsyncWaitHandle.WaitOne();
   double price = m_stockService.EndgetStockPrice(m_handle);
messageBox.Text += "The price is: " + price + system.Environment.NewLine;  
}

 


从现象上看,和同步调用相比你并没有得到好处。程序等待的时候仍然处于"挂起"状态。但是在有些情况下,"等待"还是有它的特色的。比如说你可以连续调用三个WebMethod,如果每个函数费时5秒,那么使用同步的话总共会使用15秒钟。但如果使用异步的话,你可能只要等待5秒钟。当然这要使用WaitHandle提供的WaitAll()函数。如下所示:


private void button7_Click(object sender, System.EventArgs e)
{
   if(this.m_stockService ==null)
m_stockService = new localhost.StockPrice();
   IAsyncResult[] handles = new IAsyncResult[3];
   for(int i=0;i<3;i++)
handles[i] = m_stockService.BegingetStockPrice("IBM",null,null);
   messageBox.Text = "3 function is called" + System.Environment.NewLine;
   WaitHandle[] WaitHandles =
{handles[0].AsyncWaitHandle, handles[1].AsyncWaitHandle,
handles[2].AsyncWaitHandle};
   //函数被阻拦,直到3个函数都执行完毕。WaitAny()函数情况类似,但有一个函数完成后
   //程序就解阻,继续往下执行
WaitHandle.WaitAll(WaitHandles);
   double[] prices = new double[3];
   for(int i=0;i<3;i++)
   {
prices[i] = m_stockService.EndgetStockPrice(handles[i]);  
messageBox.Text += "The price is: " + prices[i] +
System.Environment.NewLine;
   }
}

 


3.使用回调函数(CallBack)

看到现在,你可能还没有感到满意。因为你异步调用了函数后,还要手工检查函数是否执行完毕,或者要处于等待状态。能否让函数完成后,自动显示结果或是做其它操作呢?答案是"能"的。回调函数就是做这种事情的。

还记得我们前面说的例子吗。"王秘书,给我打壶开水去。打回来后给我沏杯茶()"。王秘书打水完成后,还会执行沏杯茶()这个回调函数。如果你原意,你可以让王秘书干其它任何事情。如下所示:

 

private void button5_Click(object sender, System.EventArgs e)
{
   if(this.m_stockService ==null)
  m_stockService = new localhost.StockPrice();
   //生成回调函数
   AsyncCallback cb = new AsyncCallback (this.callback);
   m_stockService.BegingetStockPrice("IBM",cb,DateTime.Now);
   messageBox.Text = "The function is called" + System.Environment.NewLine;
}

private void callback(IAsyncResult handle)
{
   double price = m_stockService.EndgetStockPrice(handle);
   lock(this)
   {
messageBox.Text += "The price is: " + price + ". Reques time: " +
      handle.AsyncState.ToString() + ", and returned at: " +
 DateTime.Now.ToString() +
   System.Environment.NewLine;
   }
}

 

 

如果你喝茶比较讲究,你可能会说,"王秘书,给我打壶开水去()。打回来后给我沏杯茶(),要龙井茶"。那么这个"龙井茶"是否可以传给王秘书呢?这样她在打回开水后沏茶的时候就知道用什么茶叶了。答案是可以的。如上面的例程所示,DateTime.Now在异步调用的时候被传递给了新的线程。在回调函数里面,这个参数可以拿出来使用。handle.AsyncState.就是这个被传进的参数。.NET规定这个参数可以是任意一个对象。所以如果有多个参数要传递的话,可以使用数组,HashTable等等。理论上讲,你可以传递任何东西。


线程间同步协调问题


从上面的例子你可以看出,异步调用的本质是由另外一个线程在真正执行函数的调用,不管是你自己直接生成的线程还是.NET提供的。当一旦进入多线程编程后,最重要的问题就是线程间的协调和同步。要高度注意的是线程间的问题往往很隐蔽,很难发现。在单处理器平台上也许不会出现问题,但移植到多处理器平台上后也许就会显现出来。.NET提供了许多方法来进行多线程的保护和协调。限于篇幅,这里不一一赘诉了。


异步的WebService


上文讨论的是如何在客户端异步的调用服务器端的WebService。但是客户端的努力还终究是有一些局限的。比如在客户端调用一个费时的WebService时,你很可能希望客户端有个"状态条"来不断的指示函数调用的进度,并且还可以随时"放弃"调用。这个任务单凭客户端程序就很难就决。这种情形就要求服务器端能提供异步服务了。任何开发异步的WebService是一个比较复杂的问题,笔者将在后文中再作介绍。谢谢!

所属分类:编程相关.Net编程    作者:新浪博客    时间:2010-11-20 0:00:00

文章导航