与其他.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的异步模式--全面介绍》



这篇随笔就先告一段落,期待我的下一篇系列随笔。

作者:tkb至简 出处:http://www.cnblogs.com/farb/

QQ:782762625

欢迎各位多多交流!

本文版权归作者和博客园共有,欢迎转载。未经作者同意下,必须在文章页面明显标出原文链接及作者,否则保留追究法律责任的权利。
如果您认为这篇文章还不错或者有所收获,可以点击右下角的【推荐】按钮,因为你的支持是我继续写作,分享的最大动力!

时间: 2024-08-06 16:05:07

与其他.Net异步模式和类型进行互操作的相关文章

实现基于Task的异步模式

返回该系列目录<基于Task的异步模式--全面介绍> 生成方法 编译器生成 在.NET Framework 4.5中,C#编译器实现了TAP.任何标有async关键字的方法都是异步方法,编译器会使用TAP执行必要的转换从而异步地实现方法.这样的方法应该返回Task或者Task<TResult>类型.在后者的案例中,方法体应该返回一个TResult,且编译器将确保通过返回的Task<TResult>是可利用的.相似地,方法体内未经处理的异常会被封送到输出的task,造成返

MSDN搬运 之 [基于事件的异步模式]

基于事件的异步模式概述 那些同时执行多项任务.但仍能响应用户交互的应用程序通常需要实施一种使用多线程的设计方案.System.Threading 命名空间提供了创建高性能多线程应用程序所必需的所有工具,但要想有效地使用这些工具,需要有丰富的使用多线程软件工程的经验.对于相对简单的多线程应用程序,BackgroundWorker 组件提供了一个简单的解决方案.对于更复杂的异步应用程序,请考虑实现一个符合基于事件的异步模式的类. 基于事件的异步模式具有多线程应用程序的优点,同时隐匿了多线程设计中固有

Task的异步模式

Task的异步模式 返回该系列目录<基于Task的异步模式--全面介绍> 生成方法 编译器生成 在.NET Framework 4.5中,C#编译器实现了TAP.任何标有async关键字的方法都是异步方法,编译器会使用TAP执行必要的转换从而异步地实现方法.这样的方法应该返回Task或者Task<TResult>类型.在后者的案例中,方法体应该返回一个TResult,且编译器将确保通过返回的Task<TResult>是可利用的.相似地,方法体内未经处理的异常会被封送到输

实践基于Task的异步模式

Await 返回该系列目录<基于Task的异步模式--全面介绍> 在API级别,实现没有阻塞的等待的方法是提供callback(回调函数).对于Tasks来说,这是通过像ContinueWith的方法实现的.基于语言的异步支持通过允许在正常控制流内部等待异步操作隐藏callbacks,具有和编译器生成的代码相同的API级别的支持. 在.Net 4.5,C#直接异步地支持等待的Task和Task<TResult>,在C#中使用"await"关键字.如果等待一个Ta

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

四.基于事件的异步模式(设计层面) 基于事件的C#异步编程模式是比IAsyncResult模式更高级的一种异步编程模式,也被用在更多的场合.该异步模式具有以下优点: ·                  “在后台”执行耗时任务(例如下载和数据库操作),但不会中断您的应用程序. ·                  同时执行多个操作,每个操作完成时都会接到通知(在通知中可以区分是完成了哪个操作). ·                  等待资源变得可用,但不会停止(“挂起”)您的应用程序. ·  

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

实现异步处理的方法很多,经常用的有基于委托的方式,今天记录的是基于事件的异步模式.利用BackgroundWorker组件可以很轻松的实现异步处理,并且该组件还支持事件的取消.进度报告等功能.本文以计算两个数X.Y的和为例. 程序界面如下图,其中三个文本框分别为两个加数和处理结果,两个按钮为计算和取消,按钮下方为进度条. 引入BackgroundWorker组件,为DoWork.ProgressChanged.RunWorkerCompleted三个事件指定方法.将WorkerReportsPr

基于事件的异步模式(EAP)

什么是EAP异步编程模式 EAP基于事件的异步模式是.net 2.0提出来的,实现了基于事件的异步模式的类将具有一个或者多个以Async为后缀的方法和对应的Completed事件,并且这些类都支持异步方法的取消.进度报告和报告结果.然而.net中并不是所有的类都支持EAP,总结起来有以下17个类支持EAP异步. System.Object的派生类型: System.Activies.WorkflowInvoke System.Deployment.Application.ApplicationD

.Net中的几种异步模式

.Net中的几种异步模式 .Net中的几种异步模式基于事件的异步模式(EAP)IAsyncResult接口简单的异步模式--引入lambdaTask手动异步编程的问题 在C# 5.0引入async之前,存在几种异步编程模式,比如Event-based Asynchronous Pattern.IAsyncResult接口.Task等等. 基于事件的异步模式(EAP) private void DumpWebPage(Uri uri) { WebClient webClient = new Web

事件驱动异步模式

事件驱动异步模式 前言 啥叫事件?啥叫驱动?异步又是啥玩意?这些字一个一个的我都认识,但是练起来我就不知道啥意思了,别急,往下看. 在下一篇文章中,我会专门介绍并发,同步,异步以及事件驱动变成的相关技术. Event-based asynchronous(EAP)在多线程的环境中提供了一个简单的处理方式. 它有以下几个特性 1.支持取消 2.可以安全的更新WPF或windows Form空间 3.在completion event中可以查询异常信息. 4."在后台"执行耗时任务(例如下