三、基于任务的异步模式(TAP),推荐使用
三、基于任务的异步模式(TAP),推荐使用
一、引言
当使用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>接口来实现进度报告的功能。
计算密集型任务
例如,请考虑使用呈现图像的异步方法。.任务的主体可以轮询取消标记,如果在呈现过程中收到取消请求,代码可提前退出。 此外,如果呈现启动之前收到取消请求,你需要阻止呈现操作:
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),推荐使用的更多相关文章
- C#中的异步调用及异步设计模式(三)——基于事件的异步模式
四.基于事件的异步模式(设计层面) 基于事件的C#异步编程模式是比IAsyncResult模式更高级的一种异步编程模式,也被用在更多的场合.该异步模式具有以下优点: · ...
- 基于任务的异步模式(TAP)
Task .net 4.0为我们带来了Task的异步,我们有以下三种方法创建Task. 1,Task.Factory.StartNew,比较常用. 2,Task.Run,是.net 4.5中增加的. ...
- .NET 基于任务的异步模式(Task-based Asynchronous Pattern,TAP) async await
本文内容 概述 编写异步方法 异步程序中的控制流 API 异步方法 线程 异步和等待 返回类型和参数 参考资料 下载 Demo 下载 Demo TPL 与 APM 和 EAP 结合(APM 和 EAP ...
- 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). 其次是 ...
- 实践基于Task的异步模式
Await 返回该系列目录<基于Task的异步模式--全面介绍> 在API级别,实现没有阻塞的等待的方法是提供callback(回调函数).对于Tasks来说,这是通过像ContinueW ...
- 实现基于Task的异步模式
返回该系列目录<基于Task的异步模式--全面介绍> 生成方法 编译器生成 在.NET Framework 4.5中,C#编译器实现了TAP.任何标有async关键字的方法都是异步方法,编 ...
- 基于Task的异步模式的定义
返回该系列目录<基于Task的异步模式--全面介绍> 命名,参数和返回类型 在TAP(Task-based Asynchronous Pattern)中的异步操作的启动和完成是通过一个单独 ...
- 基于事件的异步模式——BackgroundWorker
实现异步处理的方法很多,经常用的有基于委托的方式,今天记录的是基于事件的异步模式.利用BackgroundWorker组件可以很轻松的实现异步处理,并且该组件还支持事件的取消.进度报告等功能.本文以计 ...
- C#异步编程之基于任务的异步模式
http://www.cnblogs.com/afei-24/p/6757361.html该文讲了基于任务的编程,这里再详细介绍一下.一.延续任务 private async static void ...
随机推荐
- Windows下SVN回滚到旧版本(TortoiseSVN)
当发现新提交的代码有问题,然后想将某个旧的版本作为最新的版本时,可以使用回滚, 操作步骤如下: 1. 签出(CheckOut)最新版本的代码到电脑中(不能做任何修改) 2. 执行TortoiseSVN ...
- springboot使用Freemarker继承
最近需要用到Freemarker的继承.但是发现没有关于springboot配置Freemarker的继承的.所以趁现在有时间写个博客. 1. Freemarker继承介绍 Freemarker 通过 ...
- [转]Entity Framework Refresh context?
本文转自:http://stackoverflow.com/questions/20270599/entity-framework-refresh-context If you want to rel ...
- Expression Blend实例中文教程(10) - 缓冲动画快速入门Easing
随着Rich Internet application(RIA)应用技术的发展,各个公司越来越注重于项目的用户体验性,在保证其功能完善,运行稳定的基础上,绚丽的UI和人性化的操作设计会给用户带来舒适的 ...
- deblurGAN
-- main.py -- util.py -- data_loader.py -- mode.py -- DeblurGAN.py -- vgg19.py -- layer.py -- vgg19. ...
- 懒汉单例安全basedao
package Dao; import java.sql.*; public class BaseDao { private String drname = "com.mysql.jdbc. ...
- logstash结合es,日志收集
1.下载好logstash后,解压目录 2.进入bin目录,新建文件 logstash_default.conf input { tcp { port => 4560 codec => & ...
- CSS3自定义loading效果
效果: 使用CSS3完成loading的制作 css样式: <style type="text/css"> .mask { position: fixed; left: ...
- 终止ajax请求
在做搜索功能时,文本框输入文本就得请求一次数据,如果上一次的请求还没回又请求了就导致数据错误和无用的数据请求. 我们需要输入文本时候判断上一次的ajax请求是否完毕,若还没完毕就终止本次请求. 对于j ...
- Scrapy爬虫requests
requests 模块 模块的由来: 浏览器可以浏览网站, 是由于浏览器发送了requests , 各种请求.打开一个网站可能有几十到几百个请求. 从而服务器端会反馈各种因应不同请求生成的数据. 我们 ...