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

上面的程序算是"手工"异步调用。程序员控制线程的生成和多线程的同步问题。熟悉Java多线程编程的朋友会感觉非常的熟悉。但事实上,Microsoft并不鼓励你这样来写程序。因为他们认为多线程编程比较复杂而且容易出错,并且你的线程使用方法往往不够标准和优化。微软认为线程的生成和管理对一个程序的性能和质量是非常重要的,越复杂的程序就越明显。因此Microsoft创建了一整套线程生成和管理的服务,并鼓励你在此基础之上开发你的应用程序。其技术核心就是我前面提到的“秘书处(Thread pool 线程池)”。.NET替你管理这个"秘书处",它根据程序运行时的软硬件资源情况来决定"雇佣"多少"秘书"为最优。当你的程序被放置在多CPU的高档服务器上运行的时候,.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是一个比较复杂的问题,笔者将在后文中再作介绍。谢谢!
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Go语言(也称为Golang)是由Google开发的一种静态强类型、编译型的编程语言。它旨在成为一门简单、高效、安全和并发的编程语言,特别适用于构建高性能的服务器和分布式系统。以下是Go语言的一些主要特点和优势: 简洁性:Go语言的语法简单直观,易于学习和使用。它避免了复杂的语法特性,如继承、重载等,转而采用组合和接口来实现代码的复用和扩展。 高性能:Go语言具有出色的性能,可以媲美C和C++。它使用静态类型系统和编译型语言的优势,能够生成高效的机器码。 并发性:Go语言内置了对并发的支持,通过轻量级的goroutine和channel机制,可以轻松实现并发编程。这使得Go语言在构建高性能的服务器和分布式系统时具有天然的优势。 安全性:Go语言具有强大的类型系统和内存管理机制,能够减少运行时错误和内存泄漏等问题。它还支持编译时检查,可以在编译阶段就发现潜在的问题。 标准库:Go语言的标准库非常丰富,包含了大量的实用功能和工具,如网络编程、文件操作、加密解密等。这使得开发者可以更加专注于业务逻辑的实现,而无需花费太多时间在底层功能的实现上。 跨平台:Go语言支持多种操作系统和平台,包括Windows、Linux、macOS等。它使用统一的构建系统(如Go Modules),可以轻松地跨平台编译和运行代码。 开源和社区支持:Go语言是开源的,具有庞大的社区支持和丰富的资源。开发者可以通过社区获取帮助、分享经验和学习资料。 总之,Go语言是一种简单、高效、安全、并发的编程语言,特别适用于构建高性能的服务器和分布式系统。如果你正在寻找一种易于学习和使用的编程语言,并且需要处理大量的并发请求和数据,那么Go语言可能是一个不错的选择。
Go语言(也称为Golang)是由Google开发的一种静态强类型、编译型的编程语言。它旨在成为一门简单、高效、安全和并发的编程语言,特别适用于构建高性能的服务器和分布式系统。以下是Go语言的一些主要特点和优势: 简洁性:Go语言的语法简单直观,易于学习和使用。它避免了复杂的语法特性,如继承、重载等,转而采用组合和接口来实现代码的复用和扩展。 高性能:Go语言具有出色的性能,可以媲美C和C++。它使用静态类型系统和编译型语言的优势,能够生成高效的机器码。 并发性:Go语言内置了对并发的支持,通过轻量级的goroutine和channel机制,可以轻松实现并发编程。这使得Go语言在构建高性能的服务器和分布式系统时具有天然的优势。 安全性:Go语言具有强大的类型系统和内存管理机制,能够减少运行时错误和内存泄漏等问题。它还支持编译时检查,可以在编译阶段就发现潜在的问题。 标准库:Go语言的标准库非常丰富,包含了大量的实用功能和工具,如网络编程、文件操作、加密解密等。这使得开发者可以更加专注于业务逻辑的实现,而无需花费太多时间在底层功能的实现上。 跨平台:Go语言支持多种操作系统和平台,包括Windows、Linux、macOS等。它使用统一的构建系统(如Go Modules),可以轻松地跨平台编译和运行代码。 开源和社区支持:Go语言是开源的,具有庞大的社区支持和丰富的资源。开发者可以通过社区获取帮助、分享经验和学习资料。 总之,Go语言是一种简单、高效、安全、并发的编程语言,特别适用于构建高性能的服务器和分布式系统。如果你正在寻找一种易于学习和使用的编程语言,并且需要处理大量的并发请求和数据,那么Go语言可能是一个不错的选择。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值