天天看点

[你必须知道的异步编程]——基于任务的异步模式(TAP)一、引言二、什么是TAP——基于任务的异步模式介绍三、如何使用TAP——使用基于任务的异步模式来异步编程四、TAP与APM或EAP可以转换吗?——与其他异步模式的转换五、小结

本专题概要

引言

什么是TAP——基于任务的异步模式介绍

如何使用TAP——使用基于任务的异步模式来异步编程

TAP与APM或EAP可以转换吗?——与其他异步模式的转换

小结

  在上两个专题中我为大家介绍.NET 1.0中的APM和.NET 2.0中的EAP,在使用前面两种模式进行异步编程的时候,大家多多少少肯定会感觉到实现起来比较麻烦, 首先我个人觉得,当使用APM的时候,首先我们要先定义用来包装回调方法的委托,这样难免有点繁琐, 然而使用EAP的时候,我们又需要实现Completed事件和Progress事件,上面两种实现方式感觉都有点繁琐,同时微软也意思到了这点,所以在.NET 4.0中提出了一个新的异步模式——基于任务的异步模式,该模式主要使用System.Threading.Tasks.Task和Task<T>类来完成异步编程,相对于前面两种异步模式来讲,TAP使异步编程模式更加简单(因为这里我们只需要关注Task这个类的使用),同时TAP也是微软推荐使用的异步编程模式,下面就具体为大家分享下本专题的内容.

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

看完上面的介绍,我们是不是很迫不及待想知道如何自己实现一个基于任务的异步模式的异步方法的,并且希望只需要这个方法就可以完成异步操作的取消和进度报告的功能的(因为EAP中需要实现其他的事件和定义事件参数类型,这样的实现未免过于复杂),下面就基于上专题中实现的程序用基于任务的异步模式来完成下。下面就让我们实现自己的异步方法(亮点为只需要一个方法就可以完成进度报告和异步操作取消的功能):

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

<code>//  Download File</code>

<code>        </code><code>// CancellationToken 参数赋值获得一个取消请求</code>

<code>        </code><code>// progress参数负责进度报告</code>

<code>        </code><code>private</code> <code>void</code> <code>DownLoadFile(</code><code>string</code> <code>url, CancellationToken ct, IProgress&lt;</code><code>int</code><code>&gt; progress)</code>

<code>        </code><code>{</code>

<code>            </code><code>HttpWebRequest request = </code><code>null</code><code>;</code>

<code>            </code><code>HttpWebResponse response = </code><code>null</code><code>;</code>

<code>            </code><code>Stream responseStream = </code><code>null</code><code>;</code>

<code>            </code><code>int</code> <code>bufferSize = 2048;</code>

<code>            </code><code>byte</code><code>[] bufferBytes = </code><code>new</code> <code>byte</code><code>[bufferSize];</code>

<code>            </code><code>try</code>

<code>            </code><code>{</code>

<code>                </code><code>request = (HttpWebRequest)WebRequest.Create(url);</code>

<code>                </code><code>if</code> <code>(DownloadSize != 0)</code>

<code>                </code><code>{</code>

<code>                    </code><code>request.AddRange(DownloadSize);</code>

<code>                </code><code>}</code>

<code>                </code><code>response = (HttpWebResponse)request.GetResponse();</code>

<code>                </code><code>responseStream = response.GetResponseStream();</code>

<code>                </code><code>int</code> <code>readSize = 0;</code>

<code>                </code><code>while</code> <code>(</code><code>true</code><code>)</code>

<code>                    </code><code>// 收到取消请求则退出异步操作</code>

<code>                    </code><code>if</code> <code>(ct.IsCancellationRequested == </code><code>true</code><code>)</code>

<code>                    </code><code>{</code>

<code>                        </code><code>MessageBox.Show(String.Format(</code><code>"下载暂停,下载的文件地址为:{0}\n 已经下载的字节数为: {1}字节"</code><code>, downloadPath, DownloadSize));</code>

<code>                        </code><code>response.Close();</code>

<code>                        </code><code>filestream.Close();</code>

<code>                        </code><code>sc.Post((state) =&gt;</code>

<code>                        </code><code>{</code>

<code>                            </code><code>this</code><code>.btnStart.Enabled = </code><code>true</code><code>;</code>

<code>                            </code><code>this</code><code>.btnPause.Enabled = </code><code>false</code><code>;</code>

<code>                        </code><code>}, </code><code>null</code><code>);</code>

<code>                        </code><code>// 退出异步操作</code>

<code>                        </code><code>break</code><code>;</code>

<code>                    </code><code>}</code>

<code>                    </code><code>readSize = responseStream.Read(bufferBytes, 0, bufferBytes.Length);</code>

<code>                    </code><code>if</code> <code>(readSize &gt; 0)</code>

<code>                        </code><code>DownloadSize += readSize;</code>

<code>                        </code><code>int</code> <code>percentComplete = (</code><code>int</code><code>)((</code><code>float</code><code>)DownloadSize / (</code><code>float</code><code>)totalSize * 100);</code>

<code>                        </code><code>filestream.Write(bufferBytes, 0, readSize);</code>

<code>                        </code><code>// 报告进度</code>

<code>                        </code><code>progress.Report(percentComplete);</code>

<code>                    </code><code>else</code>

<code>                        </code><code>MessageBox.Show(String.Format(</code><code>"下载已完成,下载的文件地址为:{0},文件的总字节数为: {1}字节"</code><code>, downloadPath, totalSize));</code>

<code>                            </code><code>this</code><code>.btnStart.Enabled = </code><code>false</code><code>;</code>

<code>                </code><code>}  </code>

<code>            </code><code>}</code>

<code>            </code><code>catch</code> <code>(AggregateException ex)</code>

<code>                </code><code>// 因为调用Cancel方法会抛出OperationCanceledException异常</code>

<code>                </code><code>// 将任何OperationCanceledException对象都视为以处理</code>

<code>                </code><code>ex.Handle(e =&gt; e </code><code>is</code> <code>OperationCanceledException);</code>

<code>        </code><code>}</code>

这样只需要上面的一个方法,我们就可以完成上一专题中文件下载的程序,我们只需要在下载按钮的事件处理程序调用该方法和在暂停按钮的事件处理程序调用CancellationTokenSource.Cancel方法即可,具体代码为:

<code>// Start DownLoad File</code>

<code>        </code><code>private</code> <code>void</code> <code>btnStart_Click(</code><code>object</code> <code>sender, EventArgs e)</code>

<code>            </code><code>filestream = </code><code>new</code> <code>FileStream(downloadPath, FileMode.OpenOrCreate);</code>

<code>            </code><code>this</code><code>.btnStart.Enabled = </code><code>false</code><code>;</code>

<code>            </code><code>this</code><code>.btnPause.Enabled = </code><code>true</code><code>;</code>

<code>            </code><code>filestream.Seek(DownloadSize, SeekOrigin.Begin);</code>

<code>            </code><code>// 捕捉调用线程的同步上下文派生对象</code>

<code>            </code><code>sc = SynchronizationContext.Current;</code>

<code>            </code><code>cts = </code><code>new</code> <code>CancellationTokenSource();</code>

<code>            </code><code>// 使用指定的操作初始化新的 Task。</code>

<code>            </code><code>task = </code><code>new</code> <code>Task(() =&gt; Actionmethod(cts.Token), cts.Token);</code>

<code>            </code><code>// 启动 Task,并将它安排到当前的 TaskScheduler 中执行。</code>

<code>            </code><code>task.Start();</code>

<code>            </code><code>//await DownLoadFileAsync(txbUrl.Text.Trim(), cts.Token,new Progress&lt;int&gt;(p =&gt; progressBar1.Value = p));</code>

<code>        </code><code>// 任务中执行的方法</code>

<code>        </code><code>private</code> <code>void</code> <code>Actionmethod(CancellationToken ct)</code>

<code>            </code><code>// 使用同步上文文的Post方法把更新UI的方法让主线程执行</code>

<code>            </code><code>DownLoadFile(txbUrl.Text.Trim(), ct, </code><code>new</code> <code>Progress&lt;</code><code>int</code><code>&gt;(p =&gt;</code>

<code>                    </code><code>sc.Post(</code><code>new</code> <code>SendOrPostCallback((result)=&gt;progressBar1.Value=(</code><code>int</code><code>)result),p);</code>

<code>                </code><code>}));</code>

<code>        </code><code>// Pause Download</code>

<code>        </code><code>private</code> <code>void</code> <code>btnPause_Click(</code><code>object</code> <code>sender, EventArgs e)</code>

<code>            </code><code>// 发出一个取消请求</code>

<code>            </code><code>cts.Cancel();</code>

下面看看基于任务的异步模式的实现效果如何的,运行结果:

[你必须知道的异步编程]——基于任务的异步模式(TAP)一、引言二、什么是TAP——基于任务的异步模式介绍三、如何使用TAP——使用基于任务的异步模式来异步编程四、TAP与APM或EAP可以转换吗?——与其他异步模式的转换五、小结

点击确定按钮之后,Download按钮会重新变成可用,此时我们可以继续点击Download按钮来下载进行下载,下载完成之后会下载完成弹出框,运行结果如下:

[你必须知道的异步编程]——基于任务的异步模式(TAP)一、引言二、什么是TAP——基于任务的异步模式介绍三、如何使用TAP——使用基于任务的异步模式来异步编程四、TAP与APM或EAP可以转换吗?——与其他异步模式的转换五、小结

从上面的程序代码我们可以清楚的发现——基于任务的异步模式确实比前面的两种异步模式更加简单使用,所以,从.NET Framework 4.0开始,微软推荐使用TAP来实现异步编程,这里就涉及之前用APM或EAP实现的程序如何迁移到用TAP实现的问题的,同时.NET Framwwork对他们之间的转换了也做了很好的支持。

4.1 将APM转换为TAP

<code>// 大家可以对比这两种实现方式</code>

<code>        </code><code>#region 使用APM实现异步请求</code>

<code>        </code><code>private</code> <code>void</code> <code>APMWay()</code>

<code>            </code><code>WebRequest webRq = WebRequest.Create(</code><code>"http://msdn.microsoft.com/zh-CN/"</code><code>);</code>

<code>            </code><code>webRq.BeginGetResponse(result =&gt;</code>

<code>                </code><code>WebResponse webResponse = </code><code>null</code><code>;</code>

<code>                </code><code>try</code>

<code>                    </code><code>webResponse = webRq.EndGetResponse(result);</code>

<code>                    </code><code>Console.WriteLine(</code><code>"请求的内容大小为: "</code> <code>+ webResponse.ContentLength);</code>

<code>                </code><code>catch</code> <code>(WebException ex)</code>

<code>                    </code><code>Console.WriteLine(</code><code>"异常发生,异常信息为: "</code> <code>+ ex.GetBaseException().Message);</code>

<code>                </code><code>finally</code>

<code>                    </code><code>if</code> <code>(webResponse != </code><code>null</code><code>)</code>

<code>                        </code><code>webResponse.Close();</code>

<code>            </code><code>}, </code><code>null</code><code>);</code>

<code>        </code><code>#endregion</code>

<code>        </code><code>#region 使用FromAsync方法将APM转换为TAP</code>

<code>        </code><code>private</code> <code>void</code> <code>APMswitchToTAP()</code>

<code>            </code><code>Task.Factory.FromAsync&lt;WebResponse&gt;(webRq.BeginGetResponse, webRq.EndGetResponse, </code><code>null</code><code>, TaskCreationOptions.None).</code>

<code>                </code><code>ContinueWith(t =&gt;</code>

<code>                    </code><code>WebResponse webResponse = </code><code>null</code><code>;</code>

<code>                    </code><code>try</code>

<code>                        </code><code>webResponse = t.Result;</code>

<code>                        </code><code>Console.WriteLine(</code><code>"请求的内容大小为: "</code> <code>+ webResponse.ContentLength);</code>

<code>                    </code><code>catch</code> <code>(AggregateException ex)</code>

<code>                        </code><code>if</code> <code>(ex.GetBaseException() </code><code>is</code> <code>WebException)</code>

<code>                            </code><code>Console.WriteLine(</code><code>"异常发生,异常信息为: "</code> <code>+ ex.GetBaseException().Message);</code>

<code>                        </code><code>}</code>

<code>                        </code><code>else</code>

<code>                            </code><code>throw</code><code>;</code>

<code>                    </code><code>finally</code>

<code>                        </code><code>if</code> <code>(webResponse != </code><code>null</code><code>)</code>

<code>                            </code><code>webResponse.Close();</code>

<code>                </code><code>});</code>

上面代码演示了使用APM的原始实现方式以及如何使用FromAsync方法把APM的实现方式转换为TAP的实现方法,把这两种方式放在一起,一是可以帮助大家做一个对比,使大家更容易明白APM与TAP的转换,二是大家也可以通过上面的对比明白TAP与APM的区别。

4.2 将EAP转化为TAP

处理APM可以升级为用TAP来实现外,对于EAP,我们同样可以对其转换为TAP的方式,下面代码演示了如何将EAP转换为TAP的实现方式:

<code>#region 将EAP转换为TAP的实现方式</code>

<code>            </code><code>// webClient类支持基于事件的异步模式(EAP)</code>

<code>            </code><code>WebClient webClient = </code><code>new</code> <code>WebClient();</code>

<code>            </code><code>// 创建TaskCompletionSource和它底层的Task对象</code>

<code>            </code><code>TaskCompletionSource&lt;</code><code>string</code><code>&gt; tcs = </code><code>new</code> <code>TaskCompletionSource&lt;</code><code>string</code><code>&gt;();</code>

<code>            </code><code>// 一个string下载好之后,WebClient对象会应发DownloadStringCompleted事件</code>

<code>            </code><code>webClient.DownloadStringCompleted += (sender, e) =&gt;</code>

<code>                </code><code>// 下面的代码是在GUI线程上执行的</code>

<code>                </code><code>// 设置Task状态</code>

<code>                </code><code>if</code> <code>(e.Error != </code><code>null</code><code>)</code>

<code>                    </code><code>// 试图将基础Tasks.Task&lt;TResult&gt;转换为Tasks.TaskStatus.Faulted状态</code>

<code>                    </code><code>tcs.TrySetException(e.Error);</code>

<code>                </code><code>else</code> <code>if</code> <code>(e.Cancelled)</code>

<code>                    </code><code>// 试图将基础Tasks.Task&lt;TResult&gt;转换为Tasks.TaskStatus.Canceled状态</code>

<code>                    </code><code>tcs.TrySetCanceled();</code>

<code>                </code><code>else</code>

<code>                    </code><code>// 试图将基础Tasks.Task&lt;TResult&gt;转换为TaskStatus.RanToCompletion状态。</code>

<code>                    </code><code>tcs.TrySetResult(e.Result);</code>

<code>            </code><code>};</code>

<code>            </code><code>// 当Task完成时继续下面的Task,显示Task的状态</code>

<code>            </code><code>// 为了让下面的任务在GUI线程上执行,必须标记为TaskContinuationOptions.ExecuteSynchronously</code>

<code>            </code><code>// 如果没有这个标记,任务代码会在一个线程池线程上运行</code>

<code>            </code><code>tcs.Task.ContinueWith(t =&gt;</code>

<code>                </code><code>if</code> <code>(t.IsCanceled)</code>

<code>                    </code><code>Console.WriteLine(</code><code>"操作已被取消"</code><code>);</code>

<code>                </code><code>else</code> <code>if</code> <code>(t.IsFaulted)</code>

<code>                    </code><code>Console.WriteLine(</code><code>"异常发生,异常信息为:"</code> <code>+ t.Exception.GetBaseException().Message);</code>

<code>                    </code><code>Console.WriteLine(String.Format(</code><code>"操作已完成,结果为:{0}"</code><code>, t.Result));</code>

<code>            </code><code>}, TaskContinuationOptions.ExecuteSynchronously);</code>

<code>            </code><code>// 开始异步操作</code>

<code>            </code><code>webClient.DownloadStringAsync(</code><code>new</code> <code>Uri(</code><code>"http://msdn.microsoft.com/zh-CN/"</code><code>));</code>

<code>            </code><code>#endregion</code>

  本专题关于TAP的内容就介绍到这里了,本专题主要以实现一个文件下载程序要讲述基于任务的异步模式所带来的简便,这个也是.NET 4.0中提出TAP的原因所在吧,最后介绍了TAP与APM和EAP模式之间的转化,通过这部分大家可以清楚知道以前的异步实现如何向新的异步模式的迁移,以及从他们的转换实现代码中也可以比较他们之间的不同。然而在.NET 4.5中,微软对异步编程又做了更好的支持——提供了async和await两个关键字,这两个关键字使我们异步编程如同步编程一样的简单,彻底改变了实现异步编程所面临的委托回调,跨线程访问控件等问题,具体这部分内容,我将在下个专题中为大家介绍。

<a href="http://down.51cto.com/data/2362835" target="_blank">附件:http://down.51cto.com/data/2362835</a>

     本文转自LearningHard 51CTO博客,原文链接:http://blog.51cto.com/learninghard/1200926,如需转载请自行联系原作者

继续阅读