术语解释:

APM               异步编程模型, Asynchronous Programming Model

EAP                基于事件的异步编程模式, Event-based Asynchronous Pattern

TAP                基于任务的异步编程模式, Task-based Asynchronous Pattern

一、异步编程

APM即异步编程模型的简写(Asynchronous Programming Model),大家在写代码的时候或者查看.NET 的类库的时候肯定会经常看到和使用以BeginXXX和EndXXX类似的方法,其实你在使用这些方法的时候,你就再使用异步编程模型来编写程序。异步编写模型是一种模式,该模式允许用更少的线程去做更多的操作,.NET Framework很多类也实现了该模式,同时我们也可以自定义类来实现该模式,(也就是在自定义的类中实现返回类型为IAsyncResult接口的BeginXXX方法和EndXXX方法)另外委托类型也定义了BeginInvoke和EndInvoke方法,并且我们使用WSDL.exe和SvcUtil.exe工具来生成Web服务的代理类型时,也会生成使用了APM的BeginXxx和EndXxx方法。下面就具体就拿FileStream类的BeginReadEndRead方法来介绍下下异步编程模型的实现。

读取的同步方法:

public override int Read(byte[] array, int offset, int count )

读取过程中,执行此方法会阻塞主线程,最明显的就是造成界面卡死。

读取的异步方法:

// 开始异步读操作
// 前面的3个参数和同步方法代表的意思一样,这里就不说了,可以看到这里多出了2个参数
// userCallback代表当异步IO操作完成时,你希望由一个线程池线程执行的方法,该方法必须匹配AsyncCallback委托
// stateObject代表你希望转发给回调方法的一个对象的引用,在回调方法中,可以查询IAsyncResult接口的AsyncState属性来访问该对象
public override IAsyncResult BeginRead(byte[] array, int offset, int numBytes, AsyncCallback userCallback, Object stateObject)
 
读取完成后如果不需要进行其他操作,可以设置为null
 

EndXxx方法——结束异步操作介绍

  前面介绍完了BeginXxx方法,我们看到所有BeginXxx方法返回的都是实现了IAsyncResult接口的一个对象,并不是对应的同步方法所要得到的结果的。此时我们需要调用对应的EndXxx方法来结束异步操作,并向该方法传递IAsyncResult对象,EndXxx方法的返回类型就是和同步方法

一样的。例如,FileStreamEndRead方法返回一个Int32来代表从文件流中实际读取的字节数。

异步调用的结果:

对于访问异步操作的结果,APM提供了四种方式供开发人员选择:

  1. 在调用BeginXxx方法的线程上调用EndXxx方法来得到异步操作的结果,但是这种方式会阻塞调用线程,知道操作完成之后调用线程才继续运行

  2. 查询IAsyncResultAsyncWaitHandle属性,从而得到WaitHandle,然后再调用它的WaitOne方法来使一个线程阻塞并等待操作完成再调用EndXxx方法来获得操作的结果。

  3. 循环查询IAsyncResultIsComplete属性,操作完成后再调用EndXxx方法来获得操作返回的结果。

  4. 使用 AsyncCallback委托来指定操作完成时要调用的方法,在操作完成后调用的方法中调用EndXxx操作来获得异步操作的结果。(最为常用)

异步编程模型的本质:

异步编程模型这个模式,是微软利用委托和线程池帮助我们实现的一个模式(该模式利用一个线程池线程去执行一个操作,在FileStream类BeginRead方法中就是执行一个读取文件操作,该线程池线程会立即将控制权返回给调用线程,此时线程池线程在后台进行这个异步操作;异步操作完成之后,通过回调函数来获取异步操作返回的结果。此时就是利用委托的机制。所以说异步编程模式时利用委托和线程池线程搞出来的模式,包括后面的基于事件的异步编程和基于任务的异步编程,还有C# 5中的async和await关键字,都是利用这委托和线程池搞出来的。他们的本质都一样,后面方法使异步编程更加简单。)

  1. using System;
  2. using System.Collections.Generic;
  3. using System.ComponentModel;
  4. using System.Data;
  5. using System.Drawing;
  6. using System.IO;
  7. using System.Linq;
  8. using System.Net;
  9. using System.Runtime.Remoting.Messaging;
  10. using System.Text;
  11. using System.Windows.Forms;
  12.  
  13. //https://msdn.microsoft.com/zh-cn/library/system.net.httpwebrequest.begingetresponse.aspx
  14.  
  15. namespace WindowsFormsApplication1
  16. {
  17. public class RequestState
  18. {
  19. // This class stores the State of the request.
  20. const int BUFFER_SIZE = ;
  21. public StringBuilder requestData;
  22. public byte[] BufferRead;
  23. public HttpWebRequest request;
  24. public HttpWebResponse response;
  25. public Stream streamResponse;
  26. public Stream filestream;
  27. public string savepath;
  28. public RequestState()
  29. {
  30. BufferRead = new byte[BUFFER_SIZE];
  31. requestData = new StringBuilder("");
  32. request = null;
  33. streamResponse = null;
  34. }
  35. }
  36.  
  37. public partial class Form1 : Form
  38. {
  39.  
  40. // 定义用来实现异步编程的委托
  41. private delegate string AsyncMethodCaller(string fileurl);
  42.  
  43. public Form1()
  44. {
  45. InitializeComponent();
  46. tbUrl.Text = "http://download.microsoft.com/download/7/0/3/703455ee-a747-4cc8-bd3e-98a615c3aedb/dotNetFx35setup.exe";
  47.  
  48. // 允许跨线程调用
  49. // 实际开发中不建议这样做的,违背了.NET 安全规范
  50. CheckForIllegalCrossThreadCalls = false;
  51. }
  52.  
  53. private void btnDownLoad_Click(object sender, EventArgs e)
  54. {
  55. rtbContent.Text = "Download............";
  56. if (tbUrl.Text == string.Empty)
  57. {
  58. MessageBox.Show("Please input valid download file url");
  59. return;
  60. }
  61. AsyncMethodCaller methodCaller = new AsyncMethodCaller(DownLoadFileSync);
  62. methodCaller.BeginInvoke(tbUrl.Text.Trim(), GetResult, null);
  63. }
  64.  
  65. // 同步下载文件的方法
  66. // 该方法会阻塞主线程,使用户无法对界面进行操作
  67. // 在文件下载完成之前,用户甚至都不能关闭运行的程序。
  68. private string DownLoadFileSync(string url)
  69. {
  70. // Create an instance of the RequestState
  71. RequestState requestState = new RequestState();
  72.  
  73. try
  74. {
  75. // Initialize an HttpWebRequest object
  76. HttpWebRequest myHttpWebRequest = (HttpWebRequest)WebRequest.Create(url);
  77. // assign HttpWebRequest instance to its request field.
  78. requestState.request = myHttpWebRequest;
  79. requestState.response = (HttpWebResponse)myHttpWebRequest.GetResponse();
  80. requestState.streamResponse = requestState.response.GetResponseStream();
  81. int readSize = requestState.streamResponse.Read(requestState.BufferRead, , requestState.BufferRead.Length);
  82.  
  83. while (readSize > )
  84. {
  85. requestState.filestream.Write(requestState.BufferRead, , readSize);
  86. readSize = requestState.streamResponse.Read(requestState.BufferRead, , requestState.BufferRead.Length);
  87. }
  88. // 执行该方法的线程是线程池线程,该线程不是与创建richTextBox控件的线程不是一个线程
  89. // 如果不把 CheckForIllegalCrossThreadCalls 设置为false,该程序会出现“不能跨线程访问控件”的异常
  90. return string.Format("The Length of the File is: {0}", requestState.filestream.Length) + string.Format("\nDownLoad Completely, Download path is: {0}", requestState.savepath);
  91. }
  92. catch (Exception e)
  93. {
  94. return string.Format("Exception occurs in DownLoadFileSync method, Error Message is:{0}", e.Message);
  95. }
  96. finally
  97. {
  98. //requestState.response.Close();
  99. //requestState.filestream.Close();
  100. }
  101. }
  102. // 异步操作完成时执行的方法
  103. private void GetResult(IAsyncResult result)
  104. {
  105. AsyncMethodCaller caller = (AsyncMethodCaller)((AsyncResult)result).AsyncDelegate;
  106. // 调用EndInvoke去等待异步调用完成并且获得返回值
  107. // 如果异步调用尚未完成,则 EndInvoke 会一直阻止调用线程,直到异步调用完成
  108. string returnstring = caller.EndInvoke(result);
  109. //sc.Post(ShowState,resultvalue);
  110. rtbContent.Text = returnstring;
  111. }
  112. }
  113. }
  1. using System;
  2. using System.Collections.Generic;
  3. using System.ComponentModel;
  4. using System.Data;
  5. using System.Drawing;
  6. using System.IO;
  7. using System.Linq;
  8. using System.Net;
  9. using System.Runtime.Remoting.Messaging;
  10. using System.Text;
  11. using System.Windows.Forms;
  12.  
  13. //https://msdn.microsoft.com/zh-cn/library/system.net.httpwebrequest.begingetresponse.aspx
  14.  
  15. namespace WindowsFormsApplication1
  16. {
  17. public class RequestState
  18. {
  19. // This class stores the State of the request.
  20. const int BUFFER_SIZE = ;
  21. public StringBuilder requestData;
  22. public byte[] BufferRead;
  23. public HttpWebRequest request;
  24. public HttpWebResponse response;
  25. public Stream streamResponse;
  26. public Stream filestream;
  27. public string savepath;
  28. public RequestState()
  29. {
  30. BufferRead = new byte[BUFFER_SIZE];
  31. requestData = new StringBuilder("");
  32. request = null;
  33. streamResponse = null;
  34. }
  35. }
  36.  
  37. public partial class Form1 : Form
  38. {
  39.  
  40. // 定义用来实现异步编程的委托
  41. private delegate string AsyncMethodCaller(string fileurl);
  42.  
  43. public Form1()
  44. {
  45. InitializeComponent();
  46. tbUrl.Text = "http://download.microsoft.com/download/7/0/3/703455ee-a747-4cc8-bd3e-98a615c3aedb/dotNetFx35setup.exe";
  47.  
  48. // 允许跨线程调用
  49. // 实际开发中不建议这样做的,违背了.NET 安全规范
  50. CheckForIllegalCrossThreadCalls = false;
  51. }
  52.  
  53. private void btnDownLoad_Click(object sender, EventArgs e)
  54. {
  55. rtbContent.Text = "Download............";
  56. if (tbUrl.Text == string.Empty)
  57. {
  58. MessageBox.Show("Please input valid download file url");
  59. return;
  60. }
  61. AsyncMethodCaller methodCaller = new AsyncMethodCaller(DownLoadFileSync);
  62. methodCaller.BeginInvoke(tbUrl.Text.Trim(), GetResult, null);
  63. }
  64.  
  65. // 同步下载文件的方法
  66. // 该方法会阻塞主线程,使用户无法对界面进行操作
  67. // 在文件下载完成之前,用户甚至都不能关闭运行的程序。
  68. private string DownLoadFileSync(string url)
  69. {
  70. // Create an instance of the RequestState
  71. RequestState requestState = new RequestState();
  72.  
  73. try
  74. {
  75. // Initialize an HttpWebRequest object
  76. HttpWebRequest myHttpWebRequest = (HttpWebRequest)WebRequest.Create(url);
  77. // assign HttpWebRequest instance to its request field.
  78. requestState.request = myHttpWebRequest;
  79. requestState.response = (HttpWebResponse)myHttpWebRequest.GetResponse();
  80. requestState.streamResponse = requestState.response.GetResponseStream();
  81. int readSize = requestState.streamResponse.Read(requestState.BufferRead, , requestState.BufferRead.Length);
  82.  
  83. while (readSize > )
  84. {
  85. requestState.filestream.Write(requestState.BufferRead, , readSize);
  86. readSize = requestState.streamResponse.Read(requestState.BufferRead, , requestState.BufferRead.Length);
  87. }
  88. // 执行该方法的线程是线程池线程,该线程不是与创建richTextBox控件的线程不是一个线程
  89. // 如果不把 CheckForIllegalCrossThreadCalls 设置为false,该程序会出现“不能跨线程访问控件”的异常
  90. return string.Format("The Length of the File is: {0}", requestState.filestream.Length) + string.Format("\nDownLoad Completely, Download path is: {0}", requestState.savepath);
  91. }
  92. catch (Exception e)
  93. {
  94. return string.Format("Exception occurs in DownLoadFileSync method, Error Message is:{0}", e.Message);
  95. }
  96. finally
  97. {
  98. //requestState.response.Close();
  99. //requestState.filestream.Close();
  100. }
  101. }
  102. // 异步操作完成时执行的方法
  103. private void GetResult(IAsyncResult result)
  104. {
  105. AsyncMethodCaller caller = (AsyncMethodCaller)((AsyncResult)result).AsyncDelegate;
  106. // 调用EndInvoke去等待异步调用完成并且获得返回值
  107. // 如果异步调用尚未完成,则 EndInvoke 会一直阻止调用线程,直到异步调用完成
  108. string returnstring = caller.EndInvoke(result);
  109. //sc.Post(ShowState,resultvalue);
  110. rtbContent.Text = returnstring;
  111. }
  112. }
  113. }

当我们调用实现基于事件的异步模式的类的 XxxAsync方法时,即代表开始了一个异步操作,该方法调用完之后会使一个线程池线程去执行耗时的操作,所以当UI线程调用该方法时,当然也就不会堵塞UI线程了。并且基于事件的异步模式是建立了APM的基础之上的(这也是我在上一专题中详解介绍APM的原因),而APM又是建立了在委托之上的

二、基于事件的异步编程

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

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

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

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

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

对于相对简单的应用程序可以直接用 .Net 2.0 新增的 BackgroundWorker 组件来很方便的实现,对于更复杂的异步应用程序则需要自己实现一个符合基于事件的C#异步编程模式的类。在实现基于事件的异步模式的设计前,需要了解基于事件的异步模式的实现原理是什么。基于事件的异步模式需要以下三个类型的帮助。

AsyncOperation:提供了对异步操作的生存期进行跟踪的功能,包括操作进度通知和操作完成通知,并确保在正确的线程或上下文中调用客户端的事件处理程序。

public void Post(SendOrPostCallback d,Object arg);

public void PostOperationCompleted(SendOrPostCallback d,Object arg);

通过在异步辅助代码中调用Post方法把进度和中间结果报告给用户,如果是取消异步任务或提示异步任务已完成,则通过调用PostOperationCompleted方法结束异步操作的跟踪生命期。在PostOperationCompleted方法调用后,AsyncOperation对象变得不再可用,再次访问将引发异常。在此有个问题:在该异步模式中,通过AsyncOperation的Post函数来通知进度的时候,是如何使SendOrPostCallback委托在UI线程上执行的?针对该问题下文有具体分析。

AsyncOperationManager:为AsyncOperation对象的创建提供了便捷方式,通过CreateOperation方法可以创建多个AsyncOperation实例,实现对多个异步操作进行跟踪。

WindowsFormsSynchronizationContext:该类继承自SynchronizationContext类型,提供 Windows 窗体应用程序模型的同步上下文。该类型是基于事件异步模式通信的核心。之所以说该类型是基于事件异步模式的通信核心,是因为该类型解决了“保证SendOrPostCallback委托在UI线程上执行”的问题。它是如何解决的?请看AsyncOperation类型的Post方法的实现:

  1. /// <summary>
  2. /// AsyncOperation类型的Post方法的实现
  3. /// </summary>
  4. public void Post(SendOrPostCallback d, object arg)
  5. {
  6. this.VerifyNotCompleted();
  7. this.VerifyDelegateNotNull(d);
  8. this.syncContext.Post(d, arg);
  9. }

在AsyncOperation类型的Post方法中,直接调用了SynchronizationContext类型的Post方法,再看该Post方法的实现:

  1. /// <summary>
  2. /// WindowsFormsSynchronizationContext类型的Post方法的实现
  3. /// </summary>
  4. public override void Post(SendOrPostCallback d, object state)
  5. {
  6. if (this.controlToSendTo != null)
  7. {
  8. this.controlToSendTo.BeginInvoke(d, new object[] { state }); //此处保证了SendOrPostCallBack委托在UI线程上执行
  9.  
  10. }
  11. }

例子:

  1. using System;
  2. using System.Collections.Generic;
  3. using System.Text;
  4. using System.ComponentModel;
  5. using System.Collections.Specialized;
  6. using System.Threading;
  7.  
  8. namespace test
  9. {
  10. /// <summary>
  11. /// 任务1的进度通知代理
  12. /// </summary>
  13. /// <param name="sender"></param>
  14. /// <param name="e"></param>
  15. public delegate void Work1ProgressChangedEventHandler(object sender, Work1ProgressChangedEventArgs e);
  16. /// <summary>
  17. /// 任务1的进度通知参数
  18. /// </summary>
  19. /// <param name="sender"></param>
  20. /// <param name="e"></param>
  21. public delegate void Work1CompletedEventHandler(object sender, Work1CompletedEventArgs e);
  22.  
  23. public class BasedEventAsyncWorker
  24. {
  25. private delegate void WorkerEventHandler(int maxNumber, AsyncOperation asyncOp);
  26. private HybridDictionary userStateToLifetime = new HybridDictionary();
  27.  
  28. public BasedEventAsyncWorker()
  29. { }
  30.  
  31. #region DoWork1的基于事件的异步调用
  32. public void DoWork1Async(object userState, int maxNumber)
  33. {
  34. AsyncOperation asyncOp = AsyncOperationManager.CreateOperation(userState);
  35.  
  36. //userStateToLifetime有可能会同时被多线程访问,在此需要lock进行同步处理
  37. lock (userStateToLifetime.SyncRoot)
  38. {
  39. if (userStateToLifetime.Contains(userState))
  40. {
  41. throw new ArgumentException(
  42. "userState parameter must be unique",
  43. "userState");
  44. }
  45.  
  46. userStateToLifetime[userState] = asyncOp;
  47. }
  48.  
  49. //异步开始任务1
  50. WorkerEventHandler workerDelegate = new WorkerEventHandler(DoWork1);
  51. workerDelegate.BeginInvoke(maxNumber, asyncOp, null, null);
  52. }
  53.  
  54. private void DoWork1(int maxNumber, AsyncOperation asyncOp)
  55. {
  56. Exception e = null;
  57.  
  58. //判断该userState的任务仍在处理中
  59. if (!TaskCanceled(asyncOp.UserSuppliedState))
  60. {
  61. try
  62. {
  63. int n = ;
  64. int percentage = ;
  65. while (n < maxNumber && !TaskCanceled(asyncOp.UserSuppliedState))
  66. {
  67. Thread.Sleep(); //模拟耗时操作
  68. percentage = (int)((float)n / (float)maxNumber * );
  69. Work1ProgressChangedEventArgs progressChanageArgs =
  70. new Work1ProgressChangedEventArgs(maxNumber, percentage, asyncOp.UserSuppliedState);
  71. //任务1的进度通知
  72. asyncOp.Post(new SendOrPostCallback(Work1ReportProgressCB), progressChanageArgs);
  73. n++;
  74. }
  75. }
  76. catch (Exception ex)
  77. {
  78. e = ex;
  79. }
  80. }
  81.  
  82. this.Work1Complete(e, TaskCanceled(asyncOp.UserSuppliedState), asyncOp);
  83. }
  84.  
  85. private void Work1Complete(Exception exception, bool canceled, AsyncOperation asyncOp)
  86. {
  87. if (!canceled)
  88. {
  89. lock (userStateToLifetime.SyncRoot)
  90. {
  91. userStateToLifetime.Remove(asyncOp.UserSuppliedState);
  92. }
  93. }
  94.  
  95. Work1CompletedEventArgs e = new Work1CompletedEventArgs(exception, canceled, asyncOp.UserSuppliedState);
  96.  
  97. //通知指定的任务已经完成
  98. asyncOp.PostOperationCompleted(new SendOrPostCallback(Work1CompleteCB), e);
  99.  
  100. //调用 PostOperationCompleted 方法来结束异步操作的生存期。
  101. //为某个特定任务调用此方法后,再调用其相应的 AsyncOperation 对象会引发异常。
  102. }
  103.  
  104. private void Work1ReportProgressCB(object state)
  105. {
  106. Work1ProgressChangedEventArgs e = state as Work1ProgressChangedEventArgs;
  107.  
  108. OnWork1ProgressChanged(e);
  109. }
  110.  
  111. private void Work1CompleteCB(object state)
  112. {
  113. Work1CompletedEventArgs e = state as Work1CompletedEventArgs;
  114.  
  115. OnWork1Completed(e);
  116. }
  117.  
  118. #region Work1的进度通知和任务完成的事件
  119. public event Work1ProgressChangedEventHandler Work1ProgressChanged;
  120. protected virtual void OnWork1ProgressChanged(Work1ProgressChangedEventArgs e)
  121. {
  122. Work1ProgressChangedEventHandler temp = this.Work1ProgressChanged;
  123. if (temp != null)
  124. {
  125. temp(this, e);
  126. }
  127. }
  128.  
  129. public event Work1CompletedEventHandler Work1Completed;
  130. protected virtual void OnWork1Completed(Work1CompletedEventArgs e)
  131. {
  132. Work1CompletedEventHandler temp = this.Work1Completed;
  133. if (temp != null)
  134. {
  135. temp(this, e);
  136. }
  137. }
  138. #endregion
  139. #endregion
  140.  
  141. /// <summary>
  142. /// 取消指定userState的任务执行
  143. /// </summary>
  144. /// <param name="userState"></param>
  145. public void CancelAsync(object userState)
  146. {
  147. AsyncOperation asyncOp = userStateToLifetime[userState] as AsyncOperation;
  148. if (asyncOp != null)
  149. {
  150. lock (userStateToLifetime.SyncRoot)
  151. {
  152. userStateToLifetime.Remove(userState);
  153. }
  154. }
  155. }
  156.  
  157. /// <summary>
  158. /// 判断指定userState的任务是否已经被结束。返回值:true 已经结束; false 还没有结束
  159. /// </summary>
  160. /// <param name="userState"></param>
  161. /// <returns></returns>
  162. private bool TaskCanceled(object userState)
  163. {
  164. return (userStateToLifetime[userState] == null);
  165. }
  166.  
  167. }
  168.  
  169. public class Work1ProgressChangedEventArgs :ProgressChangedEventArgs
  170. {
  171. private int totalWork = ;
  172.  
  173. public Work1ProgressChangedEventArgs(int totalWork, int progressPercentage, object userState)
  174. : base(progressPercentage, userState)
  175. {
  176. this.totalWork = totalWork;
  177. }
  178.  
  179. /// <summary>
  180. /// Work1的总工作量
  181. /// </summary>
  182. public int TotalWork
  183. {
  184. get
  185. {
  186. return totalWork;
  187. }
  188. }
  189. }
  190.  
  191. public class Work1CompletedEventArgs : AsyncCompletedEventArgs
  192. {
  193. public Work1CompletedEventArgs(Exception e, bool canceled, object state)
  194. : base(e, canceled, state)
  195. {
  196. }
  197.  
  198. }
  199.  
  200. }

三、基于任务的异步编程

基于任务的异步模式(Task-based Asynchronous Pattern,TAP)之所以被微软所推荐,主要就它使用简单,基于任务的异步模式使用单个方法来表示异步操作的开始和完成,然而异步编程模型(APM)却要求BeginXxx和EndXxx两个方法来分别表示异步操作的开始和完成(这样使用起来就复杂了),然而,基于事件的异步模式(EAP)要求具有Async后缀的方法和一个或多个事件、事件处理程序和事件参数。看到这里,是不是大家都有这样一个疑问的——我们怎样区分.NET类库中的类实现了基于任务的异步模式呢? 这个识别方法很简单,当看到类中存在TaskAsync为后缀的方法时就代表该类实现了TAP, 并且基于任务的异步模式同样也支持异步操作的取消和进度的报告的功能,但是这两个实现都不像EAP中实现的那么复杂,因为如果我们要自己实现EAP的类,我们需要定义多个事件和事件处理程序的委托类型和事件的参数(具体可以查看上一专题中的BackgroundWorker剖析部分),但是在TAP实现中,我们只需要通过向异步方法传入CancellationToken参数,因为在异步方法内部会对这个参数的IsCancellationRequested属性进行监控,当异步方法收到一个取消请求时,异步方法将会退出执行(具体这点可以使用反射工具查看WebClient的DownloadDataTaskAsync方法,同时也可以参考我后面部分自己实现基于任务的异步模式的异步方法。),在TAP中,我们可以通过IProgress<T>接口来实现进度报告的功能,具体实现可以参考我后面的程序部分。

目前我还没有找到在.NET 类库中实现了基于任务的异步模式的哪个类提供进度报告的功能,下面的将为大家演示这个实现,并且也是这个程序的亮点,同时通过自己实现TAP的异步方法来进一步理解基于任务的异步模式。

如何基于任务异步编程

  1. // Download File
  2. // CancellationToken 参数赋值获得一个取消请求
  3. // progress参数负责进度报告
  4. private void DownLoadFile(string url, CancellationToken ct, IProgress<int> progress)
  5. {
  6. HttpWebRequest request = null;
  7. HttpWebResponse response = null;
  8. Stream responseStream = null;
  9. int bufferSize = ;
  10. byte[] bufferBytes = new byte[bufferSize];
  11. try
  12. {
  13. request = (HttpWebRequest)WebRequest.Create(url);
  14. if (DownloadSize != )
  15. {
  16. request.AddRange(DownloadSize);
  17. }
  18. response = (HttpWebResponse)request.GetResponse();
  19. responseStream = response.GetResponseStream();
  20. int readSize = ;
  21. while (true)
  22. {
  23. // 收到取消请求则退出异步操作
  24. if (ct.IsCancellationRequested == true)
  25. {
  26. MessageBox.Show(String.Format("下载暂停,下载的文件地址为:{0}\n 已经下载的字节数为: {1}字节", downloadPath, DownloadSize));
  27. response.Close();
  28. filestream.Close();
  29. sc.Post((state) =>
  30. {
  31. this.btnStart.Enabled = true;
  32. this.btnPause.Enabled = false;
  33. }, null);
  34. // 退出异步操作
  35. break;
  36. }
  37. readSize = responseStream.Read(bufferBytes, , bufferBytes.Length);
  38. if (readSize > )
  39. {
  40. DownloadSize += readSize;
  41. int percentComplete = (int)((float)DownloadSize / (float)totalSize * );
  42. filestream.Write(bufferBytes, , readSize);
  43. // 报告进度
  44. progress.Report(percentComplete);
  45. }
  46. else
  47. {
  48. MessageBox.Show(String.Format("下载已完成,下载的文件地址为:{0},文件的总字节数为: {1}字节", downloadPath, totalSize));
  49. sc.Post((state) =>
  50. {
  51. this.btnStart.Enabled = false;
  52. this.btnPause.Enabled = false;
  53. }, null);
  54. response.Close();
  55. filestream.Close();
  56. break;
  57. }
  58. }
  59. }
  60. catch (AggregateException ex)
  61. {
  62. // 因为调用Cancel方法会抛出OperationCanceledException异常
  63. // 将任何OperationCanceledException对象都视为以处理
  64. ex.Handle(e => e is OperationCanceledException);
  65. }
  66. }

四、Async和await关键字(C#5.0,.net4.5以后)

”async”和”await”:你用我用,才有用;你用我不用,等于没用。

五、异步编程几种方式的比较,引同行文章http://www.cnblogs.com/durow/p/4826653.html

C#异步编程の-------异步编程模型(APM)的更多相关文章

  1. 转:[你必须知道的异步编程]——异步编程模型(APM)

    本专题概要: 引言 你知道APM吗? 你想知道如何使用异步编程模型编写代码吗? 使用委托也可以实现异步编程,你知道否? 小结 一.引言 在前面的C#基础知识系列中介绍了从C#1.0——C#4.0中一些 ...

  2. [你必须知道的异步编程]——异步编程模型(APM)

    本专题概要: 引言 你知道APM吗? 你想知道如何使用异步编程模型编写代码吗? 使用委托也可以实现异步编程,你知道否? 小结 一.引言 在前面的C#基础知识系列中 介绍了从C#1.0——C#4.0中一 ...

  3. 一、异步编程模型(APM)

    一.概念 APM即异步编程模式的简写(Asynchronous Programming Model).大家在写代码的时候或者查看.NET 的类库的时候肯定会经常看到和使用以BeginXXX和EndXX ...

  4. Paip.Php Java 异步编程。推模型与拉模型。响应式(Reactive)”编程FutureData总结... 1

    Paip.Php  Java 异步编程.推模型与拉模型.响应式(Reactive)"编程FutureData总结... 1.1.1       异步调用的实现以及角色(:调用者 提货单) F ...

  5. 关于Web开发里并发、同步、异步以及事件驱动编程的相关技术

    一.开篇语 我的上篇文章<关于如何提供Web服务端并发效率的异步编程技术>又成为了博客园里“编辑推荐”的文章,这是对我写博客很大的鼓励,也许是被推荐的原因很多童鞋在这篇文章里发表了评论,有 ...

  6. python2.0_s12_day9_事件驱动编程&异步IO

    论事件驱动与异步IO 事件驱动编程是一种编程范式,这里程序的执行流由外部事件来决定.它的特点是包含一个事件循环,当外部事件发生时使用回调机制来触发相应的处理.另外两种常见的编程范式是(单线程)同步以及 ...

  7. C++笔记-并发编程 异步任务(async)

    转自 https://www.cnblogs.com/diysoul/p/5937075.html 参考:https://zh.cppreference.com/w/cpp/thread/lock_g ...

  8. C++并发编程 异步任务

    C++并发编程 异步任务 异步任务 std::async (1) std::async 会返回一个 std::future 对象, 这个对象持有最终计算出来的结果. 当需要这个值时, 只需要调用对象的 ...

  9. C# 并发编程 (异步编程与多线程)

    并发:同时做多件事情 多线程:并发的一种形式,它采用多个线程来执行程序. 并行处理:把正在执行的大量的任务分割成小块,分配给多个同时运行的线程.并行处理是多线程的一种,而多线程是并发的一种. 异步编程 ...

随机推荐

  1. yum安装时遇到的问题

    在yum 安装出现下面的提示的时候,需要安装GPG key warning: rpmts_HdrFromFdno: Header V3 DSA signature: NOKEY, key ID e85 ...

  2. 通过 URL 打开 Activity

    为每个 Activity 绑定一个 url 可以方便的让第三方 app 直接打开这些 Activity.也可以方便在 app 内部进行页面跳转,解耦. 背景 举一个常见的案例,假设我们有个产品 A,产 ...

  3. 搭建jenkins集群

    搭建jenkins集群是为了解决单点服务器存在的性能瓶颈,也有业务的需要,比如:java服务打包的环境我们需要linux,ios打包的服务器需要mac机. 一.创建agent节点 1.打开 系统管理- ...

  4. JavaWeb学习 (十)————Cookie

    一.会话的概念 会话可简单理解为:用户开一个浏览器,点击多个超链接,访问服务器多个web资源,然后关闭浏览器,整个过程称之为一个会话. 有状态会话:一个同学来过教室,下次再来教室,我们会知道这个同学曾 ...

  5. FFmpeg使用基础

    本文为作者原创,转载请注明出处:https://www.cnblogs.com/leisure_chn/p/10297002.html 本文介绍FFmpeg最基础的概念,了解FFmpeg的简单使用,帮 ...

  6. OpenCV设置保存图像压缩率

    OpenCV写入静态图片时,imwrite函数第三个参数可以设置压缩率,默认值为95. cv::Mat inImage= cv::imread("lena.jpg"); vecto ...

  7. 数据库内连接GROUP BY查询外键表数据行的总数

    最近看了看SQL,刚好遇到这个问题. INNER JOIN [外键表] ON [主键表] 内链接,用 GROUP BY 分组外键数据,COUNT(*)计算该外键数据总行数,最后用 ORDER BY 排 ...

  8. yapi部署文档

    windows 下 yapi部署文档 安装nodejs 安装mongodb 安装yapi 介绍 随着 web 技术的发展,前后端分离成为越来越多互联网公司构建应用的方式.前后端分离的优势是一套 Api ...

  9. Java文件编译出现 “编码 GBK 的不可映射字符”

    俗话说,温故而知新.本打算用dos回忆一下基础知识,没想到把自己绊倒了. 用Dos,当然就要回归原始,用记事本啦.下面用一个小练习,演示我遇到的绊脚石.之后,解决了简直笑死. 报错: Java文件编译 ...

  10. Docker 容器备份例子

    # 在 node1 执行 nginx 程序,挂载本地的目录 docker pull nginx:stable-alpine mkdir /data/html echo "hello worl ...