基于任务的编程模型TAP

一、引言

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

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

基于任务的异步模式(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的异步方法来进一步理解基于任务的异步模式。

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

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

        //  Download File
        // CancellationToken 参数赋值获得一个取消请求
        // progress参数负责进度报告
        private void DownLoadFile(string url, CancellationToken ct, IProgress<int> progress)
        {
            HttpWebRequest request = null;
            HttpWebResponse response = null;
            Stream responseStream = null;
            int bufferSize = 2048;
            byte[] bufferBytes = new byte[bufferSize];
            try
            {
                request = (HttpWebRequest)WebRequest.Create(url);
                if (DownloadSize != 0)
                {
                    request.AddRange(DownloadSize);
                }
                response = (HttpWebResponse)request.GetResponse();
                responseStream = response.GetResponseStream();
                int readSize = 0;
                while (true)
                {
                    // 收到取消请求则退出异步操作
                    if (ct.IsCancellationRequested == true)
                    {
                        MessageBox.Show(String.Format("下载暂停,下载的文件地址为:{0}\n 已经下载的字节数为: {1}字节", downloadPath, DownloadSize));

                        response.Close();
                        filestream.Close();
                        sc.Post((state) =>
                        {
                            this.btnStart.Enabled = true;
                            this.btnPause.Enabled = false;
                        }, null);
                        // 退出异步操作
                        break;
                    }
                    readSize = responseStream.Read(bufferBytes, 0, bufferBytes.Length);
                    if (readSize > 0)
                    {
                        DownloadSize += readSize;
                        int percentComplete = (int)((float)DownloadSize / (float)totalSize * 100);
                        filestream.Write(bufferBytes, 0, readSize);
                        // 报告进度
                        progress.Report(percentComplete);
                    }
                    else
                    {
                        MessageBox.Show(String.Format("下载已完成,下载的文件地址为:{0},文件的总字节数为: {1}字节", downloadPath, totalSize));
                        sc.Post((state) =>
                        {
                            this.btnStart.Enabled = false;
                            this.btnPause.Enabled = false;
                        }, null);
                        response.Close();
                        filestream.Close();
                        break;
                    }
                }
            }
            catch (AggregateException ex)
            {
                // 因为调用Cancel方法会抛出OperationCanceledException异常
                // 将任何OperationCanceledException对象都视为以处理
                ex.Handle(e => e is OperationCanceledException);
            }
        }
        // Start DownLoad File
        private void btnStart_Click(object sender, EventArgs e)
        {
            filestream = new FileStream(downloadPath, FileMode.OpenOrCreate);
            this.btnStart.Enabled = false;
            this.btnPause.Enabled = true;
            filestream.Seek(DownloadSize, SeekOrigin.Begin);
            // 捕捉调用线程的同步上下文派生对象
            sc = SynchronizationContext.Current;
            cts = new CancellationTokenSource();
            // 使用指定的操作初始化新的 Task。
            task = new Task(() => Actionmethod(cts.Token), cts.Token);
            // 启动 Task,并将它安排到当前的 TaskScheduler 中执行。
            task.Start();
            //await DownLoadFileAsync(txbUrl.Text.Trim(), cts.Token,new Progress<int>(p => progressBar1.Value = p));
        }
        // 任务中执行的方法
        private void Actionmethod(CancellationToken ct)
        {
            // 使用同步上文文的Post方法把更新UI的方法让主线程执行
            DownLoadFile(txbUrl.Text.Trim(), ct, new Progress<int>(p =>
                {
                    sc.Post(new SendOrPostCallback((result)=>progressBar1.Value=(int)result),p);
                }));
        }
        // Pause Download
        private void btnPause_Click(object sender, EventArgs e)
        {
            // 发出一个取消请求
            cts.Cancel();
        }

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

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

4.1 将APM转换为TAP

在System.Threading.Tasks命名空间中,有一个TaskFactory(任务工程)类,我们正可以利用该类的FromAsync方法来实现将APM转换为TAP,下面就用基于任务的异步模式来实现在异步编程模型博文中例子。

        // 大家可以对比这两种实现方式
        #region 使用APM实现异步请求
        private void APMWay()
        {
            WebRequest webRq = WebRequest.Create("http://msdn.microsoft.com/zh-CN/");
            webRq.BeginGetResponse(result =>
            {
                WebResponse webResponse = null;
                try
                {
                    webResponse = webRq.EndGetResponse(result);
                    Console.WriteLine("请求的内容大小为: " + webResponse.ContentLength);
                }
                catch (WebException ex)
                {
                    Console.WriteLine("异常发生,异常信息为: " + ex.GetBaseException().Message);
                }
                finally
                {
                    if (webResponse != null)
                    {
                        webResponse.Close();
                    }
                }
            }, null);
        }
        #endregion
        #region 使用FromAsync方法将APM转换为TAP
        private void APMswitchToTAP()
        {
            WebRequest webRq = WebRequest.Create("http://msdn.microsoft.com/zh-CN/");
            Task.Factory.FromAsync<WebResponse>(webRq.BeginGetResponse, webRq.EndGetResponse, null, TaskCreationOptions.None).
                ContinueWith(t =>
                {
                    WebResponse webResponse = null;
                    try
                    {
                        webResponse = t.Result;
                        Console.WriteLine("请求的内容大小为: " + webResponse.ContentLength);
                    }
                    catch (AggregateException ex)
                    {
                        if (ex.GetBaseException() is WebException)
                        {
                            Console.WriteLine("异常发生,异常信息为: " + ex.GetBaseException().Message);
                        }
                        else
                        {
                            throw;
                        }
                    }
                    finally
                    {
                        if (webResponse != null)
                        {
                            webResponse.Close();
                        }
                    }
                });
        }
        #endregion

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

4.2 将EAP转化为TAP

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

#region 将EAP转换为TAP的实现方式

            // webClient类支持基于事件的异步模式(EAP)
            WebClient webClient = new WebClient();

            // 创建TaskCompletionSource和它底层的Task对象
            TaskCompletionSource<string> tcs = new TaskCompletionSource<string>();

            // 一个string下载好之后,WebClient对象会应发DownloadStringCompleted事件
            webClient.DownloadStringCompleted += (sender, e) =>
            {
                // 下面的代码是在GUI线程上执行的
                // 设置Task状态
                if (e.Error != null)
                {
                    // 试图将基础Tasks.Task<TResult>转换为Tasks.TaskStatus.Faulted状态
                    tcs.TrySetException(e.Error);
                }
                else if (e.Cancelled)
                {
                    // 试图将基础Tasks.Task<TResult>转换为Tasks.TaskStatus.Canceled状态
                    tcs.TrySetCanceled();
                }
                else
                {
                    // 试图将基础Tasks.Task<TResult>转换为TaskStatus.RanToCompletion状态。
                    tcs.TrySetResult(e.Result);
                }
            };

            // 当Task完成时继续下面的Task,显示Task的状态
            // 为了让下面的任务在GUI线程上执行,必须标记为TaskContinuationOptions.ExecuteSynchronously
            // 如果没有这个标记,任务代码会在一个线程池线程上运行
            tcs.Task.ContinueWith(t =>
            {
                if (t.IsCanceled)
                {
                    Console.WriteLine("操作已被取消");
                }
                else if (t.IsFaulted)
                {
                    Console.WriteLine("异常发生,异常信息为:" + t.Exception.GetBaseException().Message);
                }
                else
                {
                    Console.WriteLine(String.Format("操作已完成,结果为:{0}", t.Result));
                }
            }, TaskContinuationOptions.ExecuteSynchronously);

            // 开始异步操作
            webClient.DownloadStringAsync(new Uri("http://msdn.microsoft.com/zh-CN/"));
            #endregion

原文地址:https://www.cnblogs.com/zhlziliaoku/p/9217306.html

时间: 2024-11-13 09:31:28

基于任务的编程模型TAP的相关文章

基于SOA的编程模型

1.webservice是SOA架构的一种实现 ====================================================================================================== Spring-WS编程模型: 面向契约的开发:xml--对象(而不是对象---xml) 编程模型: 1.定义消息契约xml(数据契约+操作契约) 2.生成xsd 3.编写消息端点 4.配置序列化OXM 5.生成wsdl-操作契约 ========

基于页面的编程模型

两个实例 这两天写WinForm程序和Android程序. 我发现这两者的开发有很多相似之处. 他们都是基于页面的编程模型. WinForm程序,前端是.desinger.cs文件,后端是.cs文件.他们两个文件实际上是同一个类.这个类继承于Form类. Android程序,前端是.layout的一个xml文件,后端是.cs文件(用的Xamarin.Android).其中的类继承于Activity类. 都是前置代码管界面,后置代码管功能.没有前后端分离. Form和Activity都是页面. 一

(1)网络编程的常识 (2)基于tcp协议的编程模型 (3)tcp协议和udp协议的比较 (4)基于udp协议的编程模型

1.网络编程的常识 目前主流的网络通讯软件有:微信.QQ.YY.陌陌.探探.飞信.阿里旺旺.... 在吗? 1.1 七层网络模型(熟悉) 为了保证数据传递的可靠安全等等,ISO(国际标准委员会组织)将数据的传递从逻辑上划分为以下七层: 应用层.表示层.会话层.传输层.网络层.数据链路层.物理层. 在发送数据之前要按照上述七层协议从上到下一层一层进行加包处理,再发送出去; 在接收数据之后要按照上述七层协议从下到上一层一层进行拆包处理,再解析出来: 1.2 常用的协议(熟悉) http协议 - 超文

.net异步编程の-------异步编程模型(APM)

术语解释: APM               异步编程模型, Asynchronous Programming Model EAP                基于事件的异步编程模式, Event-based Asynchronous Pattern TAP                基于任务的异步编程模式, Task-based Asynchronous Pattern 一.异步编程 APM即异步编程模型的简写(Asynchronous Programming Model),大家在写代

.NET &ldquo;底层&rdquo;异步编程模式&mdash;&mdash;异步编程模型

本文内容 异步编程类型 环境 异步编程模型(APM) 参考资料 首先澄清,异步编程模式(Asynchronous Programming Patterns)与异步编程模型(Asynchronous Programming Model,APM),它们的中文翻译只差一个字,英文名称差在最后一个单词,看英文一个是 Pattern,一个是 Model.Model 比 Pattern 更具体.前者是一个统称,比后者含义要广,前者包含三个模型,而 APM 只是它其中一个而已. 个人理解,异步编程模型(APM

异步编程模型(APM)

一.概念 APM即异步编程模式的简写(Asynchronous Programming Model).大家在写代码的时候或者查看.NET 的类库的时候肯定会经常看到和使用以BeginXXX和EndXXX类似的方法,其实你在使用这些方法的时候,你就再使用异步编程模型来编写程序.NET Framework很多类也实现了该模式,同时我们也可以自定义类来实现该模式,(也就是在自定义的类中实现返回类型为IAsyncResult接口的BeginXXX方法和EndXXX方法),另外委托类型也定义了BeginI

Unity基于响应式编程(Reactive programming)入门

系列目录 [Unity3D基础]让物体动起来①--基于UGUI的鼠标点击移动 [Unity3D基础]让物体动起来②--UGUI鼠标点击逐帧移动 时光煮雨 Unity3D让物体动起来③—UGUI DoTween&Unity Native2D实现 时光煮雨 Unity3D实现2D人物动画① UGUI&Native2D序列帧动画 时光煮雨 Unity3D实现2D人物动画② Unity2D 动画系统&资源效率 背景 前有慕容小匹夫的一篇<解构C#游戏框架uFrame兼谈游戏架构设计&

多线程编程模型

在学习muduo网络库前,应该先熟悉一下多线程网络服务编程模型.在6.6.2节介绍了11种方案.方案0到方案4用的是阻塞I/O.方案5到方案11用的都是阻塞I/O. 方案0: accept+read/write 方案0不是并发模型,只是一个循环处理.用代码表示的话,可以表示为: while(true) { int fd=accept(--); read(fd,--) or write(fd--); close(fd); } 一次只能处理一个连接,第一个连接处理完毕后,才可以进入下一次循环,否则阻

基于servlet网络编程

1.我们知道了基于http协议的网络编程模型和基于套接字的网路编程模型(相对于我的博客上写的),那么我们将使用更加便捷的servlet网络编程. 基于http协议的网络编程,我的博客上面需要我们自己写客户端,基于套接字的网络编程我的博客上面需要我们自己写客户端和服务器端,现在我们用servlet编程,不需要我们写客户端和服务器端,我们只需要在tomcat容器里写我们的处理程序就行,响应请求由tomcat来完成. 2.如何在tomcat上部署项目.打开tomcat文件,找到webapp文件里面的例