当前位置:首页 > Windows程序 > 正文

【温故知新】C#基于事件的异步模式(EAP)

2021-03-27 Windows程序

在开发winform和调用asp.net的web service引用的时候,会出现许多命名为 MethodNameAsync 的方法。

例如:

winform的按钮点击

this.button1.Click += new System.EventHandler(this.button1_Click);
private void button1_Click(object sender, EventArgs e) {   //dosomething }

这就是基于事件的异步编程模式,它实现了不影响主线程的情况下异步调用耗时方法,在完成的时候通过事件进行函数回调,一般情况下,我们都应该使用该模式来公开类的异步方法。

那什么时候需要使用IAsyncResult 模式呢?微软给出了很好的答案,见https://msdn.microsoft.com/zh-cn/library/ms228966(v=vs.110).aspx

接下来就让我们通过代码实现一个基于事件的异步模式

代码场景

我们模拟一个下载器,下载我喜爱的影片,过程中实时展示下载进度,并且在下载完成后进行提醒。

核心代码如下:

public class Downloader { //声明事件参数 public class DownloadCompletedEventArgs : System.ComponentModel.AsyncCompletedEventArgs { private string m_result; public DownloadCompletedEventArgs(string result, Exception error, bool cancelled, Object userState) : base(error, cancelled, userState) { m_result = result; } public string Result { get { //只读属性在返回属性值之前应调用 RaiseExceptionIfNecessary 方法。 如果组件的异步辅助代码将某一异常指定给 Error 属性或将 Cancelled 属性设置为 true,则该属性将在客户端尝试读取它的值时引发异常。 这会防止客户端因异步操作失败而访问可能无效的属性。 RaiseExceptionIfNecessary(); return m_result; } } } //声明委托 public delegate void ProgressChangedEventHandler( ProgressChangedEventArgs e);//ProgressChangedEventArgs自带有了。 public delegate void DownloadCompletedEventHandler(object sender, DownloadCompletedEventArgs e); //内部下载处理委托 private delegate string DownLoadHandler(string url, string name, AsyncOperation asyncOp); //声明事件 public event ProgressChangedEventHandler ProgressChanged; public event DownloadCompletedEventHandler DownloadCompleted; //声明SendOrPostCallback委托,通过AsyncOperation.post会将这些调用正确地封送到应用程序模型的合适线程或上下文。 private SendOrPostCallback onProgressChangedDelegate; private SendOrPostCallback onDownloadCompletedDelegate; /// <summary> /// 构造函数 /// </summary> public Downloader() { onProgressChangedDelegate = new SendOrPostCallback(onProgressChanged); onDownloadCompletedDelegate = new SendOrPostCallback(onDownloadComplete); } /// <summary> /// 通过AsyncOperation调用onProgressChangedDelegate委托关联该函数,,保证运行在合适线程 /// </summary> /// <param></param> private void onProgressChanged(object state) { if (ProgressChanged != null) { ProgressChangedEventArgs e = state as ProgressChangedEventArgs; ProgressChanged(e); } } private void onDownloadComplete(object state) { if (DownloadCompleted != null) { DownloadCompletedEventArgs e = state as DownloadCompletedEventArgs; DownloadCompleted(this, e); } } /// <summary> /// 异步下载文件 /// </summary> /// <param></param> /// <param></param> public void DownloadAsync(string url, string name) { //url不能为null if (url == null) { throw new ArgumentNullException("url"); } //userSuppliedState 参数来唯一地标识每个调用,以便区分执行异步操作的过程中所引发的事件。 //不唯一的任务 ID 可能会导致您的实现无法正确报告进度和其他事件。 代码中应检查是否存在不唯一的任务 ID,并且在检测到不唯一的任务 ID 时引发 SystemArgumentException。 //由于我们不用监控异步操作状态,所以参数设为null AsyncOperation asyncOp = AsyncOperationManager.CreateOperation(null); //异步委托调用download,如果不想再声明DownLoadHandler委托,用Action或Fun代替也行。 DownLoadHandler dh = new DownLoadHandler(DownLoad); dh.BeginInvoke("", "乔布斯传", asyncOp, new AsyncCallback(DownloadCallBack), asyncOp); } private void DownloadCallBack(IAsyncResult iar) { AsyncResult aresult = (AsyncResult)iar; DownLoadHandler dh = aresult.AsyncDelegate as DownLoadHandler; string r = dh.EndInvoke(iar); AsyncOperation ao = iar.AsyncState as AsyncOperation; //特定任务调用此方法后,再调用其相应的 AsyncOperation 对象会引发异常。 ao.PostOperationCompleted(onDownloadCompletedDelegate, new DownloadCompletedEventArgs(r, null, false, null)); } /// <summary> /// 提供给外部调用的同步方法 /// </summary> /// <param></param> /// <param></param> /// <returns></returns> public string DownLoad(string url, string name) { return DownLoad(url, name, null); } private string DownLoad(string url, string name, AsyncOperation asyncOp) { //url不能为null if (url == null) { throw new ArgumentNullException("url"); } for (int i = 0; i < 10; i++) { int p = i * 10; Debug.WriteLine("执行线程:" + Thread.CurrentThread.ManagedThreadId + ",传输进度:" + p + "%"); Thread.Sleep(1000); //不为空则是异步 if (asyncOp != null) { //在适合于应用程序模型的线程或上下文中调用委托。 asyncOp.Post(onProgressChangedDelegate, new ProgressChangedEventArgs(p, null)); } } return name + "文件下载完成!"; } }

在客户端调用:

温馨提示: 本文由Jm博客推荐,转载请保留链接: https://www.jmwww.net/file/68704.html