与其他.Net异步模式和类型进行互操作
返回該系列目錄《基于Task的異步模式--全面介紹》
Tasks和異步編程模型APM(Tasks and the Asynchronous Programming Model)
從APM到Tasks
APM模式依賴兩個對應(yīng)的方法來表示一個異步操作:BeginMethodName和EndMethodName。在高級別,begin方法接受的參數(shù)和相應(yīng)的同步方法MethodName的參數(shù)是一樣的,而且還接受一個AsyncCallback和一個object state。begin方法然后返回IAsyncResult,IAsyncResult從它的AsyncState屬性返回傳遞給begin方法的object state。異步操作完成時,IAsyncResult的IsCompleted屬性會開始返回true,且會設(shè)置它的AsyncWaitHandle屬性。而且,如果begin方法的AsyncCallback參數(shù)是非空的,那么會調(diào)用callback,且將它傳給從begin方法返回的相同的IAsyncResult。當(dāng)異步操作確實(shí)完成時,會使用EndMethodName方法連接該操作,檢索任何結(jié)果或者強(qiáng)制產(chǎn)生的異常傳播。
由于APM模式結(jié)構(gòu)的本質(zhì),構(gòu)建一個APM的包裝器來將它暴露為一個TAP實(shí)現(xiàn)是相當(dāng)容易的。實(shí)際上,.Net Framework 4 以TaskFactory.FromAsync的形式提供了轉(zhuǎn)化的幫助路線。
思考.Net 中的Stream類和BeginRead/EndRead 方法,它們都代表了同步的Read方法的APM對應(yīng)版本:
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,可實(shí)現(xiàn)該方法的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的實(shí)現(xiàn)和下面的具有同樣效果:
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
對于現(xiàn)有的基礎(chǔ)設(shè)施期望代碼實(shí)現(xiàn)APM模式的場合,能夠采取TAP實(shí)現(xiàn)以及在期待TAP實(shí)現(xiàn)的地方使用它也是很重要的。幸好有了tasks的組合性,以及Task本身實(shí)現(xiàn)IAsyncResult的事實(shí),使用一個簡單的幫助函數(shù)就可以實(shí)現(xiàn)了(這里展示的是一個Task<TResult>的擴(kuò)展,但幾乎相同的函數(shù)可能用于非泛型的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; }現(xiàn)在,想一個有TAP實(shí)現(xiàn)的場合:
public static Task<string> DownloadStringAsync(Uri url);且我們需要提供APM實(shí)現(xiàn):
public IAsyncResult BeginDownloadString(Uri url, AsyncCallback callback, object state); public string EndDownloadString(IAsyncResult asyncResult);可以通過下面代碼實(shí)現(xiàn):
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的實(shí)例MethodNameAsync方法,接收和同步方法MethodName方法相同的參數(shù),并且要實(shí)例化異步操作。實(shí)例異步操作之前,事件句柄使用相同實(shí)例上的事件注冊,然后觸發(fā)這些事件來提供進(jìn)度和完成通知。事件句柄一般都是自定義的委托類型,該委托類型利用了派生自ProgressChangedEventArgs或AsyncCompletedEventArgs的事件參數(shù)類型。
包裝一個EAP實(shí)現(xiàn)更復(fù)雜一些,因為該模式本身牽扯了比APM模式更多的變量和更少的結(jié)構(gòu)。為了演示,接下來包裝一個DownloadStringAsync方法。DownloadStringAsync接受一個Uri參數(shù),為了上報多個進(jìn)度上的統(tǒng)計數(shù)據(jù),下載時會觸發(fā)DownloadProgressChanged 事件,完成時會觸發(fā)DownloadStringCompleted 事件。最終結(jié)果是一個包含在指定Uri的頁面內(nèi)容的字符串。
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
高級的開發(fā)人員可能會發(fā)現(xiàn),WaitHandle 設(shè)置時,自己利用 WaitHandles 和線程池的 RegisterWaitForSingleObject 方法進(jìn)行異步通知,然而這本質(zhì)上不是一個異步模式 。我們可以包裝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; }使用那些之前演示的構(gòu)建于Task之上的數(shù)據(jù)結(jié)構(gòu)的技巧,相似地,構(gòu)建一個不依賴WaitHandles且完全以Task的角度工作的異步信號燈(semaphore)也是可能的。事實(shí)上,.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類實(shí)現(xiàn)了IAsyncResult,該IAsyncResult的實(shí)現(xiàn)暴露了一個返回WaitHandle的AsycnWaitHandle屬性,此WaitHandle是在Task完成時設(shè)置的。照這樣,獲得一個Task的WaitHandle可以像下面這樣實(shí)現(xiàn):
WaitHandle wh = ((IAsyncResult)task).AsyncWaitHandle;?
返回該系列目錄《基于Task的異步模式--全面介紹》
總結(jié)
以上是生活随笔為你收集整理的与其他.Net异步模式和类型进行互操作的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 女人梦到打仗逃命是什么意思
- 下一篇: 在 ASP.NET MVC 3 中应用