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;