一、异步编程模型(APM)

二、基于事件的异步编程模式(EAP)

三、基于任务的异步模式(TAP),推荐使用

四、C# 5.0 新特性——Async和Await使异步编程更简单

一、引言

  当使用APM的时候,首先我们要先定义用来包装回调方法的委托,这样难免有点繁琐, 然而使用EAP的时候,我们又需要实现Completed事件和Progress事件,上面两种实现方式感觉都有点繁琐,同时微软也意识到了这点,所以在.NET 4.0中提出了一个新的异步模式——基于任务的异步模式TAP(Task-based Asynchronous Pattern ),基于任务的异步模式 (TAP) 是基于 System.Threading.Tasks.Task 命名空间中的 System.Threading.Tasks.Task<TResult>System.Threading.Tasks 类型,这些类型用于表示任意异步操作。是用于新开发的建议的异步设计模式。

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

当看到类中存在TaskAsync为后缀的方法时就代表该类实现了TAP, 并且基于任务的异步模式同样也支持异步操作的取消和进度的报告的功能。在TAP实现中,我们只需要通过向异步方法传入CancellationToken 参数,因为在异步方法内部会对这个参数的IsCancellationRequested属性进行监控,当异步方法收到一个取消请求时,异步方法将会退出执行。在TAP中,我们可以通过IProgress<T>接口来实现进度报告的功能。

二、并行编程 - Task任务

计算密集型任务

例如,请考虑使用呈现图像的异步方法。.任务的主体可以轮询取消标记,如果在呈现过程中收到取消请求,代码可提前退出。 此外,如果呈现启动之前收到取消请求,你需要阻止呈现操作:

internal Task<Bitmap> RenderAsync(ImageData data, CancellationToken cancellationToken)
{
return Task.Run(() =>
{
var bmp = new Bitmap(data.Width, data.Height);
for(int y=0; y<data.Height; y++)
{
cancellationToken.ThrowIfCancellationRequested();
for(int x=0; x<data.Width; x++)
{
// render pixel [x,y] into bmp
}
}
return bmp;
}, cancellationToken);
}

I/O 密集型任务:

假设你想创建一个将在指定时间段后完成的任务。  例如,你可能想延迟用户界面中的活动。  System.Threading.Timer 类已提供在指定时间段后以异步方式调用委托的能力,并且你可以通过使用 TaskCompletionSource<TResult>Task<TResult> 前端放在计时器上,例如:
public static Task<DateTimeOffset> Delay(int millisecondsTimeout)
{
TaskCompletionSource<DateTimeOffset> tcs = null;
Timer timer = null; timer = new Timer(delegate
{
timer.Dispose();
tcs.TrySetResult(DateTimeOffset.UtcNow);
}, null, Timeout.Infinite, Timeout.Infinite); tcs = new TaskCompletionSource<DateTimeOffset>(timer);
timer.Change(millisecondsTimeout, Timeout.Infinite);
return tcs.Task;
}

从 .NET Framework 4.5 开始,Task.Delay 方法正是为此而提供的,并且你可以在另一个异步方法内使用它。例如,若要实现异步轮询循环:

public static async Task Poll(Uri url, CancellationToken cancellationToken,
IProgress<bool> progress)
{
while(true)
{
await Task.Delay(TimeSpan.FromSeconds(10), cancellationToken);
bool success = false;
try
{
await DownloadStringAsync(url);
success = true;
}
catch { /* ignore errors */ }
progress.Report(success);
}
}

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

就基于上专题中实现的程序用基于任务的异步模式来完成下。下面就让我们实现自己的异步方法(亮点为只需要一个方法就可以完成进度报告和异步操作取消的功能)

// 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);
}
}

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

#region 字段

private int DownloadSize = 0;
private string downloadPath = null;
private long totalSize = 0;
private FileStream filestream; private CancellationTokenSource cts = null;
private Task task = null; private SynchronizationContext sc; #endregion 字段 #region 构造函数 public FileDownLoadForm()
{
InitializeComponent();
string url = "http://download.microsoft.com/download/7/0/3/703455ee-a747-4cc8-bd3e-98a615c3aedb/dotNetFx35setup.exe";
txbUrl.Text = url;
this.btnPause.Enabled = false; // Get Total Size of the download file
GetTotalSize();
downloadPath = Environment.GetFolderPath(Environment.SpecialFolder.Desktop) + "\\" + Path.GetFileName(this.txbUrl.Text.Trim());
if (File.Exists(downloadPath))
{
FileInfo fileInfo = new FileInfo(downloadPath);
DownloadSize = (int)fileInfo.Length;
if (DownloadSize == totalSize)
{
string message = "There is already a file with the same name, "
+ "do you want to delete it? "
+ "If not, please change the local path. ";
var result = MessageBox.Show(
message,
"File name conflict: " + downloadPath,
MessageBoxButtons.OKCancel); if (result == System.Windows.Forms.DialogResult.OK)
{
File.Delete(downloadPath);
}
else
{
progressBar1.Value = (int)((float)DownloadSize / (float)totalSize * 100);
this.btnStart.Enabled = false;
this.btnPause.Enabled = false;
}
}
}
} #endregion 构造函数 #region 方法 // 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();
} // Get Total Size of File
private void GetTotalSize()
{
HttpWebRequest myHttpWebRequest = (HttpWebRequest)WebRequest.Create(txbUrl.Text.Trim());
HttpWebResponse response = (HttpWebResponse)myHttpWebRequest.GetResponse();
totalSize = response.ContentLength;
response.Close();
}

四、与其他异步模式和类型互操作

1、从 APM 到 TAP

可以使用 TaskFactory<TResult>.FromAsync 方法来实现此操作的 TAP 包装,如下所示:

public static Task<int> ReadAsync(this Stream stream,
byte[] buffer, int offset,
int count)
{
if (stream == null)
throw new ArgumentNullException("stream"); return Task<int>.Factory.FromAsync(stream.BeginRead,
stream.EndRead, buffer,
offset, count, null);
}

此实现类似于以下内容:

 public static Task<int> ReadAsync(this Stream stream,
byte [] buffer, int offset,
int count)
{
if (stream == null)
throw new ArgumentNullException("stream"); var tcs = new TaskCompletionSource<int>();
stream.BeginRead(buffer, offset, count, iar =>
{
try {
tcs.TrySetResult(stream.EndRead(iar));
}
catch(OperationCanceledException) {
tcs.TrySetCanceled();
}
catch(Exception exc) {
tcs.TrySetException(exc);
}
}, null);
return tcs.Task;
}

2、从EAP到 TAP

public static Task<string> DownloadStringAsync(Uri url)
{
var tcs = new TaskCompletionSource<string>();
var wc = new WebClient();
wc.DownloadStringCompleted += (s,e) =>
{
if (e.Error != null)
tcs.TrySetException(e.Error);
else if (e.Cancelled)
tcs.TrySetCanceled();
else
tcs.TrySetResult(e.Result);
};
wc.DownloadStringAsync(url);
return tcs.Task;
}

五、小结

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

三、基于任务的异步模式(TAP),推荐使用的更多相关文章

  1. C#中的异步调用及异步设计模式(三)——基于事件的异步模式

    四.基于事件的异步模式(设计层面) 基于事件的C#异步编程模式是比IAsyncResult模式更高级的一种异步编程模式,也被用在更多的场合.该异步模式具有以下优点: ·                 ...

  2. 基于任务的异步模式(TAP)

    Task .net 4.0为我们带来了Task的异步,我们有以下三种方法创建Task. 1,Task.Factory.StartNew,比较常用. 2,Task.Run,是.net 4.5中增加的. ...

  3. .NET 基于任务的异步模式(Task-based Asynchronous Pattern,TAP) async await

    本文内容 概述 编写异步方法 异步程序中的控制流 API 异步方法 线程 异步和等待 返回类型和参数 参考资料 下载 Demo 下载 Demo TPL 与 APM 和 EAP 结合(APM 和 EAP ...

  4. C#5.0之后推荐使用TPL(Task Parallel Libray 任务并行库) 和PLINQ(Parallel LINQ, 并行Linq). 其次是TAP(Task-based Asynchronous Pattern, 基于任务的异步模式)

    学习书籍: <C#本质论> 1--C#5.0之后推荐使用TPL(Task Parallel Libray 任务并行库) 和PLINQ(Parallel LINQ, 并行Linq). 其次是 ...

  5. 实践基于Task的异步模式

    Await 返回该系列目录<基于Task的异步模式--全面介绍> 在API级别,实现没有阻塞的等待的方法是提供callback(回调函数).对于Tasks来说,这是通过像ContinueW ...

  6. 实现基于Task的异步模式

    返回该系列目录<基于Task的异步模式--全面介绍> 生成方法 编译器生成 在.NET Framework 4.5中,C#编译器实现了TAP.任何标有async关键字的方法都是异步方法,编 ...

  7. 基于Task的异步模式的定义

    返回该系列目录<基于Task的异步模式--全面介绍> 命名,参数和返回类型 在TAP(Task-based Asynchronous Pattern)中的异步操作的启动和完成是通过一个单独 ...

  8. 基于事件的异步模式——BackgroundWorker

    实现异步处理的方法很多,经常用的有基于委托的方式,今天记录的是基于事件的异步模式.利用BackgroundWorker组件可以很轻松的实现异步处理,并且该组件还支持事件的取消.进度报告等功能.本文以计 ...

  9. C#异步编程之基于任务的异步模式

    http://www.cnblogs.com/afei-24/p/6757361.html该文讲了基于任务的编程,这里再详细介绍一下.一.延续任务 private async static void ...

随机推荐

  1. 安装和部署Jenkins

    安装和部署Jenkins 环境 操作系统:ubuntu 14.04.4 LTS 下载Jenkins wget https://mirrors.tuna.tsinghua.edu.cn/jenkins/ ...

  2. mysql linux下表名忽略大小写注意事项

    在Unix中使用lower_case_tables_name=0,在Windows中使用lower_case_tables_name=2.这样了可以保留数据库名和表名的大小写.不利之处是必须确保在Wi ...

  3. 九度oj 1437 To Fill or Not to Fill 2012年浙江大学计算机及软件工程研究生机试真题

    题目1437:To Fill or Not to Fill 时间限制:1 秒 内存限制:128 兆 特殊判题:否 提交:1488 解决:345 题目描述: With highways availabl ...

  4. Redis介绍与安装

    一.redis简介 (一)什么是redis 1.redis是一个开源的使用ANSI C语言编写.支持网络.可基于内存亦可持久化的日志型.Key-Value 内存数据库. 2.特点:读写性能强悍 支持丰 ...

  5. android socket 通讯(客户端) 发送数据

    /** ClientSocket通讯类 **/  public class ClientSocket  {     /**服务器地址*/     private String serverUrl=&q ...

  6. access 2010,语文

    access 2010*(报表) 使用报表创建:打开需要创建图形的报表----创建----报表----完成. 使用报表向导创建:创建----报表向导----选择表/查询----选择字段----设置分布 ...

  7. [android] 切换界面的通用处理

    实现不改变activity,只切换View 抽取View界面的基类 利用面向对象多态的思路,实现通用 TitleManager.java 管理标题 package com.tsh.lottery.vi ...

  8. vcfc之zk+postsql+keystore(cassandra)框架分析

    vcfc框架总结: 1 一. bus和keystore是如何协调处理的,什么样的问题是处理不了的? 1. 如果在备重启的过程中,主处理了某个时间1,备机如果同步数据呢? 二 .数据可靠性和一致性分析 ...

  9. JS对象原型的理解

    基于原型的语言 JavaScript 常被描述为一种基于原型的语言 (prototype-based language)——每个对象拥有一个原型对象,对象以其原型为模板.从原型继承方法和属性.原型对象 ...

  10. SPOJ:COT2 Count on a tree II

    题意 给定一个n个节点的树,每个节点表示一个整数,问u到v的路径上有多少个不同的整数. n=40000,m=100000 Sol 树上莫队模板题 # include <bits/stdc++.h ...