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方法链接该操做,检索任何结果或者强制产生的异常传播。html
因为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的实现和下面的具备一样效果:async
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; }
对于现有的基础设施指望代码实现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实现:this
public IAsyncResult BeginDownloadString( Uri url, AsyncCallback callback, object state); public string EndDownloadString(IAsyncResult asyncResult);
能够经过下面代码实现:url
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; }
基于事件的异步模式依赖于一个返回void的实例MethodNameAsync方法,接收和同步方法MethodName方法相同的参数,而且要实例化异步操做。实例异步操做以前,事件句柄使用相同实例上的事件注册,而后触发这些事件来提供进度和完成通知。事件句柄通常都是自定义的委托类型,该委托类型利用了派生自ProgressChangedEventArgs或AsyncCompletedEventArgs的事件参数类型。spa
包装一个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; }
高级的开发人员可能会发现,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; }
static SemaphoreSlim m_throttle = new SemaphoreSlim(N, N); static async Task DoOperation() { await m_throttle.WaitAsync(); … // do work m_throttle.Release (); }
如以前提到的,Task类实现了IAsyncResult,该IAsyncResult的实现暴露了一个返回WaitHandle的AsycnWaitHandle属性,此WaitHandle是在Task完成时设置的。照这样,得到一个Task的WaitHandle能够像下面这样实现:
WaitHandle wh = ((IAsyncResult)task).AsyncWaitHandle;