天天看点

基于事件的异步调用模式

基于事件的异步模式(EAP模式)

基于事件的C#异步编程模式是比IAsyncResult模式更高级的一种异步编程模式,也被用在更多的场合。该异步模式具有以下优点:

·                  “在后台”执行耗时任务(例如下载和数据库操作),但不会中断您的应用程序。

·                  同时执行多个操作,每个操作完成时都会接到通知(在通知中可以区分是完成了哪个操作)。

·                  等待资源变得可用,但不会停止(“挂起”)您的应用程序。

·                  使用熟悉的事件和委托模型与挂起的异步操作通信。

实现 了EAP模式的最典型组件是WebClient。

WebClient定义了以下两个同步方法用于从WEB上下载文件:

public void DownloadFile(string address, string fileName);

public void DownloadFile(Uri address, string fileName);      

为了实现异步调用,WebClient又定义了另两个对应的异步方法:

public void DownloadFileAsync(Uri address, string fileName);

public void DownloadFileAsync(Uri address, string fileName, object userToken);      

EAP规定方法名以 Async 结尾的方法是异步调用方法。

上面的方法,最后一个userToken参数前面已经说明了其作用。

其实很简单,当应用程序多次调用DownloadFileAsync方法的这个重载形式启动多个异步下载任务时,这个参数用于区分这些任务。简单地说,userToken

是异步下载任务的标识。为每个正在执行的下载任务给出唯一的标识,是程序员的责任。

为了让用户中途取消任务,WebClient定义了以下方法:

public void CancelAsync();

由于任务可以被取消,因此问题产生了:异步调用方法启动以后,调用者如何知道任务是正常结束还是被中途取消的?

答案很简单:异步调用任务结束时,实现 了EAP的组件会激发一个相应的事件。以WebClient为例,当DownloadFileAsync方法启动的异步下载任务结束时,它会激发以下事件:

public event AsyncCompletedEventHandler DownloadFileCompleted;

这一事件有一个 AsyncCompletedEventArgs 类型的参数,其中包含了重要的信息:

​​​​

public class AsyncCompletedEventArgs : EventArgs
    {

        public bool Cancelled { get; } //该值指示异步操作是否已被取消。

        public Exception Error { get; }//该值指示异步操作期间发生的错误。
    
        public object UserState { get; }//获取异步任务的唯一标识符,如果用户在启动本任务时设定了任务标识,则此属性值就是这个标识

    }      

​​​​

由此可见,只需在 DownloadFileCompleted事件的响应方法中检查一下事件参数的Cancelled属性,如果其值等于 true,就知道任务被中途取消了。

如果error属性不为null,则任务执行过程中一定引发了异常。上述两个条件都不满足时,则任务是顺利完成的。

当异步任务可能要执行很长时间时,往往需要通知用户当前工作完成的进度,为此,WebClient定义了以下事件:

public event DownloadProgressChangedEventHandler DownloadProgressChanged;

这个事件的参数中(DownloadProgressChangedEventArgs类型的对象)中包含了重要的信息。

1. TotalBytesToReceive:要传送的总字节数。

2. BytesReceived:已接收的字节数。

3.ProgressPercentage:工作完成的百分比。

掌握了以上知识,使用WebClient组件下载文件就变得非常简单,以下是一个代码框架:

​​​​

//从Web异步下载网址为FileAddress的文件

public void DownLoadFileFromWeb(string FileAddress)

{

  WebClient client = new WebClient();

  //挂接下载完成事件响应代码

  client.DownloadFileCompleted += client_DownloadFileCompleted;

  //挂接下载进度事件响应代码

  client.DownloadProgressChanged += client_DownloadProgressChanged;

  //启动异步下载文件任务

  Uri uri = new Uri(FileAddress);

  client.DownloadStringAsync(uri);

}      

​​​​

下载完成事件响应代码框架如下:

​​​​

void client_DownloadFileCompleted(object sender,AsyncCompletedEventArgs e)

{

  if(e.Cancelled)    //用户取消了操作

  {

       //处理代码。。。。

  }

  if(e.Error != null)   //有错误发生

  {

      //处理代码 

  }

  if(e.UserState != null)   //取出任务标识

  {

    //处理代码

  }

    //其它处理代码。。。



}      

​​​​

下载进度事件响应代码框架如下:

​​​​

void client_DownloadProgressChanged(object sender,DownloadProgressChangedEventArgs e)

{

   string info = "任务标识:{0},总数据:{1}字节,已下载:{2}字节,完成了{3}%。";

   info = string.Format(info , e.UserState , e.TotalBytesToReceive , e.BytesReceived , e.ProgressPercentage);

   //.....

}      

​​​​

依据前面的内容,我们可以对EAP形成以下的认识:

1.实现了EAP的组件定义 了以 Async 结尾的异步调用方法。

2.当异步调用任务结束时,会激发一个相应的事件,事件的参数包含重要的信息。

3.实现 了EAP的组件可能会提供一个用于取消异步任务的方法。

4.实现 了EAP的组件提供一个向用户报告进度的事件。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Web;
using System.Net;
using System.ComponentModel;
namespace ConsoleApplication1
{
    public class EventAsyncModel
    {
        //从Web异步下载网址为FileAddress的文件

        public void DownLoadFileFromWeb(string FileAddress)
        {
            WebClient client = new WebClient();
            //挂接下载完成事件响应代码
            client.DownloadFileCompleted += client_DownloadFileCompleted;
            //挂接下载进度事件响应代码
            client.DownloadProgressChanged += client_DownloadProgressChanged;
            //启动异步下载文件任务
            Uri uri = new Uri(FileAddress);
            client.DownloadFileAsync(uri,@"C:\QQ.EXE");

        }

        void client_DownloadFileCompleted(object sender, AsyncCompletedEventArgs e)
        {
            if (e.Cancelled)    //用户取消了操作
            {
                //处理代码。。。。
                Console.WriteLine("cancel");
            }
            if (e.Error != null)   //有错误发生
            {
                //处理代码 
                Console.WriteLine("发生错误" + e.Error.ToString());
            }
            if (e.UserState != null)   //取出任务标识
            {
                //处理代码
                Console.WriteLine("任务状态" + e.UserState.ToString());
            }
            //其它处理代码。。。
            Console.WriteLine("下载结束呢");
        }

        void client_DownloadProgressChanged(object sender, DownloadProgressChangedEventArgs e)
        {
            string info = "任务标识:{0},总数据:{1}字节,已下载:{2}字节,完成了{3}%。";
            info = string.Format(info, e.UserState, e.TotalBytesToReceive, e.BytesReceived, e.ProgressPercentage);
            Console.WriteLine(info);
        }
    }
}      

继续阅读