与其他.Net异步模式和类型进行互操作
返回该系列目录《基于Task的异步模式--全面介绍》
Tasks和异步编程模型APM(Tasks and the Asynchronous Programming Model)
从APM到Tasks
APM模式依赖两个对应的方法来表示一个异步操作:BeginMethodName和EndMethodName。在高级别,begin方法接受的参数和相应的同步方法MethodName的参数是一样的,而且还接受一个AsyncCallback和一个object state。begin方法然后返回IAsyncResult,IAsyncResult从它的AsyncState属性返回传递给begin方法的object state。异步操作完成时,IAsyncResult的IsCompleted属性会开始返回true,且会设置它的AsyncWaitHandle属性。而且,如果begin方法的AsyncCallback参数是非空的,那么会调用callback,且将它传给从begin方法返回的相同的IAsyncResult。当异步操作确实完成时,会使用EndMethodName方法连接该操作,检索任何结果或者强制产生的异常传播。
由于APM模式结构的本质,构建一个APM的包装器来将它暴露为一个TAP实现是相当容易的。实际上,.Net Framework 4 以TaskFactory.FromAsync的形式提供了转化的帮助路线。
思考.Net 中的Stream类和BeginRead/EndRead 方法,它们都代表了同步的Read方法的APM对应版本:
public int Read(
byte [] buffer, int offset, int count);
…
public IAsyncResult BeginRead(
byte [] buffer, int offset, int count,
AsyncCallback callback, object state);
public int EndRead(IAsyncResult asyncResult);
利用FromAsycn,可实现该方法的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);
}
这个使用了FromAsync的实现和下面的具有同样效果:
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;
}
从Tasks到APM
对于现有的基础设施期望代码实现APM模式的场合,能够采取TAP实现以及在期待TAP实现的地方使用它也是很重要的。幸好有了tasks的组合性,以及Task本身实现IAsyncResult的事实,使用一个简单的帮助函数就可以实现了(这里展示的是一个Task<TResult>的扩展,但几乎相同的函数可能用于非泛型的Task):
public static IAsyncResult AsApm<T>(
this Task<T> task, AsyncCallback callback, object state)
{
if (task == null) throw new ArgumentNullException(“task”);
var tcs = new TaskCompletionSource<T>(state);
task.ContinueWith(t =>
{
if (t.IsFaulted) tcs.TrySetException(t.Exception.InnerExceptions)
else if (t.IsCanceled) tcs.TrySetCanceled();
else tcs.TrySetResult(t.Result); if (callback != null) callback(tcs.Task);
}, TaskScheduler.Default);
return tcs.Task;
}
现在,想一个有TAP实现的场合:
public static Task<string> DownloadStringAsync(Uri url);
且我们需要提供APM实现:
public IAsyncResult BeginDownloadString(
Uri url, AsyncCallback callback, object state);
public string EndDownloadString(IAsyncResult asyncResult);
可以通过下面代码实现:
public IAsyncResult BeginDownloadString(
Uri url, AsyncCallback callback, object state)
{
return DownloadStringAsync(url).AsApm(callback, state);
} public string EndDownloadString(IAsyncResult asyncResult)
{
return ((Task<string>)asyncResult).Result;
}
Tasks和基于事件的异步模式EAP(Event-based Asynchronous Pattern)
基于事件的异步模式依赖于一个返回void的实例MethodNameAsync方法,接收和同步方法MethodName方法相同的参数,并且要实例化异步操作。实例异步操作之前,事件句柄使用相同实例上的事件注册,然后触发这些事件来提供进度和完成通知。事件句柄一般都是自定义的委托类型,该委托类型利用了派生自ProgressChangedEventArgs或AsyncCompletedEventArgs的事件参数类型。
包装一个EAP实现更复杂一些,因为该模式本身牵扯了比APM模式更多的变量和更少的结构。为了演示,接下来包装一个DownloadStringAsync方法。DownloadStringAsync接受一个Uri参数,为了上报多个进度上的统计数据,下载时会触发DownloadProgressChanged 事件,完成时会触发DownloadStringCompleted 事件。最终结果是一个包含在指定Uri的页面内容的字符串。
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;
}
Tasks和等待句柄(WaitHandlers)
从WaitHandlers到Tasks
高级的开发人员可能会发现,WaitHandle 设置时,自己利用 WaitHandles 和线程池的 RegisterWaitForSingleObject 方法进行异步通知,然而这本质上不是一个异步模式 。我们可以包装RegisterWaitForSingleObject来启用WaitHandle之上的任何异步等待的基于task的选择:
public static Task WaitOneAsync(this WaitHandle waitHandle)
{
if (waitHandle == null) throw new ArgumentNullException("waitHandle"); var tcs = new TaskCompletionSource<bool>();
var rwh = ThreadPool.RegisterWaitForSingleObject(waitHandle,
delegate { tcs.TrySetResult(true); }, null, -1, true);
var t = tcs.Task;
t.ContinueWith(_ => rwh.Unregister(null));
return t;
}
使用那些之前演示的构建于Task之上的数据结构的技巧,相似地,构建一个不依赖WaitHandles且完全以Task的角度工作的异步信号灯(semaphore)也是可能的。事实上,.Net 4.5中的SemaphoreSlim 类型暴露了开启这个的WaitAsync方法。
比如,之前提到的System.Threading.Tasks.Dataflow.dll中的BufferBlock<T>类型可以这样使用:
static SemaphoreSlim m_throttle = new SemaphoreSlim(N, N); static async Task DoOperation()
{
await m_throttle.WaitAsync();
… // do work
m_throttle.Release ();
}
从Tasks到WaitHandlers
如之前提到的,Task类实现了IAsyncResult,该IAsyncResult的实现暴露了一个返回WaitHandle的AsycnWaitHandle属性,此WaitHandle是在Task完成时设置的。照这样,获得一个Task的WaitHandle可以像下面这样实现:
WaitHandle wh = ((IAsyncResult)task).AsyncWaitHandle;
返回该系列目录《基于Task的异步模式--全面介绍》
与其他.Net异步模式和类型进行互操作的更多相关文章
- 基于Task的异步模式--全面介绍
今天是国庆长假第一天,也是今天十月的开始.每到这个时候都是看海的季节-一个看"人海"的季节.反正我是不想在这样一个尴尬期出去放松自己,于是不如在家写写博客,长点本领呢.今天就来给大 ...
- 三、基于任务的异步模式(TAP),推荐使用
一.引言 在上两个专题中我为大家介绍.NET 1.0中的APM和.NET 2.0中的EAP,在使用前面两种模式进行异步编程的时候,大家多多少少肯定会感觉到实现起来比较麻烦, 首先我个人觉得,当使用AP ...
- 异步编程系列06章 以Task为基础的异步模式(TAP)
p { display: block; margin: 3px 0 0 0; } --> 写在前面 在学异步,有位园友推荐了<async in C#5.0>,没找到中文版,恰巧也想提 ...
- 基于事件的异步模式(EAP)
什么是EAP异步编程模式 EAP基于事件的异步模式是.net 2.0提出来的,实现了基于事件的异步模式的类将具有一个或者多个以Async为后缀的方法和对应的Completed事件,并且这些类都支持异步 ...
- 实践基于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组件可以很轻松的实现异步处理,并且该组件还支持事件的取消.进度报告等功能.本文以计 ...
- Task的异步模式
Task的异步模式 返回该系列目录<基于Task的异步模式--全面介绍> 生成方法 编译器生成 在.NET Framework 4.5中,C#编译器实现了TAP.任何标有async关键字的 ...
随机推荐
- IE WebBrowser事件触发
<= IE6: IE6 下如果iframe很多,子框架的BeforeNavigate2,DownloadBegin,DownloadComplete,DocumentComplete可能交替出现 ...
- SaaS模式给用户带来的优势
这两年SaaS服务在中国越来越受欢迎,企业正在从使用本地化软件向SaaS服务转变.由于企业用户人力成本的上升.移动终端设备的兴起以及共享经济对企业的影响,企业采用经营设备.软件的方式也在逐渐发生着变化 ...
- spring+struts2的jar
- UITextField使用详解
转iOS中UITextField使用详解 (1) //初始化textfield并设置位置及大小 UITextField *text = [[UITextField alloc]initWithFr ...
- ilspy导致c# dll代码被窃取
不得不放弃.net框架的任何开发!!!微软现在太蛋疼了!!! 不得不放弃.net框架的任何开发!!!微软现在太蛋疼了!!! 不得不放弃.net框架的任何开发!!!微软现在太蛋疼了!!! 不得不放弃.n ...
- MS SQL查看效率语句 与PLSQL中F5功能相同
使用方法:打开SQL SERVER 查询分析器,输入以下语句: SET STATISTICS PROFILE ON SET STATISTICS IO ON SET STATISTICS TIME O ...
- 微信小程序 设计理念指南
在此处输入标题 微信小程序的几条开发建议 功能简约,场景贴近随用随走: 操作快捷方便,交互简单: 程序本身代码资源等文件大小限制在1MB之内,这是微信目前的硬限制,目的是为了使得最终到达用户设备上 ...
- 12个Linux进程管理命令介绍(转)
12个Linux进程管理命令介绍 [日期:2015-06-02] 来源:Linux中国 作者:Linux [字体:大 中 小] 执行中的程序在称作进程.当程序以可执行文件存放在存储中,并且运行的 ...
- JS-Dom概念
<!DOCTYPE HTML><html><head><meta http-equiv="Content-Type" content=&q ...
- 进阶——scrapy登录豆瓣解决cookie传递问题并爬取用户参加过的同城活动©seven_clear
最近在用scrapy重写以前的爬虫,由于豆瓣的某些信息要登录后才有权限查看,故要实现登录功能.豆瓣登录偶尔需要输入验证码,这个在以前写的爬虫里解决了验证码的问题,所以只要搞清楚scrapy怎么提交表单 ...