自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

F#與ASP.NET:使用F#實現(xiàn)基于事件的異步模式

開發(fā) 開發(fā)工具
ASP.NET MVC 2對于異步模式提供了必要的支持,使此方面的程序設(shè)計變得相對簡單一些。那么現(xiàn)在我們就來看看一般我們應(yīng)該如何來實現(xiàn)這樣的功能,以及F#是如何美化我們的生活的吧。

在之前的文章F#與ASP.NET:基于事件的異步模式與異步Action中,我們的簡單討論了.NET中兩種異步模型以及它們在異常處理上的區(qū)別,并且簡單觀察了ASP.NET MVC 2中異步Action的編寫方式。從中我們得知,ASP.NET MVC 2的異步Action并非使用了傳統(tǒng)基于Begin/End的異步編程模型,而是另一種基于事件的異步模式。

此外,ASP.NET MVC 2對于這種異步模式提供了必要的支持,使此方面的程序設(shè)計變得相對簡單一些。但是,簡單的原因主要還是在于已經(jīng)由其他組件提供了良好的,基于事件的異步模式。那么現(xiàn)在我們就來看看一般我們應(yīng)該如何來實現(xiàn)這樣的功能,以及F#是如何美化我們的生活的吧。

異步數(shù)據(jù)傳輸

我們?yōu)槭裁匆惒?,主要目的之一還是提高I/O操作的伸縮性。I/O操作主要是I/O設(shè)備的事情,這些設(shè)備在好好工作的時候,是不需要系統(tǒng)花心思進(jìn)行照看的,它們只要能夠在完成指定工作后通知系統(tǒng)就可以了。這也是異步I/O高效的原理,因為它將程序從等待I/O完成的苦悶中解脫出來,這對用戶或是系統(tǒng)來說都是一件絕好的事情。

那么說到I/O操作,最典型的場景之一便是數(shù)據(jù)傳輸了。比如有兩個數(shù)據(jù)流streamIn和streamOut,我們需要異步地從streamIn中讀取數(shù)據(jù),并異步地寫入到streamOut中。在這個過程中,我們使用一個相對較小的byte數(shù)組作為緩存空間,這樣程序在進(jìn)行數(shù)據(jù)傳輸時便不會占用太多內(nèi)存。

那么,如果現(xiàn)在需要您編寫一個組件完成這樣的數(shù)據(jù)傳輸工作,并使用標(biāo)準(zhǔn)的基于事件的異步模式釋放出來,您會怎么做?基于事件的異步模式,要求在任務(wù)完成時使用事件進(jìn)行提示。同時在出錯的時候?qū)惓ο蟊4嬖谑录膮?shù)中?,F(xiàn)在我已經(jīng)幫您寫好了這樣的事件參數(shù):

  1. public class CompletedEventArgs : EventArgs  
  2. {  
  3.     public CompletedEventArgs(Exception ex)  
  4.     {  
  5.         this.Error = ex;  
  6.     }  
  7.  
  8.     public Exception Error { get; private set; }  

那么接下來的工作就交給您了,以下代碼僅供參考:

  1. public class AsyncTransfer  
  2. {  
  3.     private Stream m_streamIn;  
  4.     private Stream m_streamOut;  
  5.  
  6.     public AsyncTransfer(Stream streamIn, Stream streamOut)  
  7.     {  
  8.         this.m_streamIn = streamIn;  
  9.         this.m_streamOut = streamOut;  
  10.     }  
  11.  
  12.     public void StartAsync()  
  13.     {  
  14.         byte[] buffer = new byte[1024];  
  15.  
  16.         this.m_streamIn.BeginRead(  
  17.             buffer, 0, buffer.Length,  
  18.             this.EndReadInputStreamCallback, buffer);  
  19.     }  
  20.  
  21.     private void EndReadInputStreamCallback(IAsyncResult ar)  
  22.     {  
  23.         var buffer = (byte[])ar.AsyncState;  
  24.         int lengthRead;  
  25.  
  26.         try  
  27.         {  
  28.             lengthRead = this.m_streamIn.EndRead(ar);  
  29.         }  
  30.         catch (Exception ex)  
  31.         {  
  32.             this.OnCompleted(ex);  
  33.             return;  
  34.         }  
  35.  
  36.         if (lengthRead <= 0)  
  37.         {  
  38.             this.OnCompleted(null);  
  39.         }  
  40.         else  
  41.         {  
  42.             try  
  43.             {  
  44.                 this.m_streamOut.BeginWrite(  
  45.                     buffer, 0, lengthRead,  
  46.                     this.EndWriteOutputStreamCallback, buffer);  
  47.             }  
  48.             catch (Exception ex)  
  49.             {  
  50.                 this.OnCompleted(ex);  
  51.             }  
  52.         }  
  53.     }  
  54.  
  55.     private void EndWriteOutputStreamCallback(IAsyncResult ar)  
  56.     {  
  57.         try  
  58.         {  
  59.             this.m_streamOut.EndWrite(ar);  
  60.  
  61.             var buffer = (byte[])ar.AsyncState;  
  62.             this.m_streamIn.BeginRead(  
  63.                 buffer, 0, buffer.Length,  
  64.                 this.EndReadInputStreamCallback, buffer);  
  65.         }  
  66.         catch (Exception ex)  
  67.         {  
  68.             this.OnCompleted(ex);  
  69.         }  
  70.     }  
  71.  
  72.     private void OnCompleted(Exception ex)  
  73.     {  
  74.         var handler = this.Completed;  
  75.         if (handler != null)  
  76.         {  
  77.             handler(this, new CompletedEventArgs(ex));  
  78.         }  
  79.     }  
  80.  
  81.     public event EventHandler<CompletedEventArgs> Completed;  

是不是很復(fù)雜的樣子?編寫異步程序,基本則意味著要將原本同步的調(diào)用拆成兩段:發(fā)起及回調(diào),這樣便讓上下文狀態(tài)的保存便的困難起來。幸運的是,C#這門語言提供了方便好用的匿名函數(shù)語法,這對于編寫一個回調(diào)函數(shù)來說已經(jīng)非常容易了。但是,如果需要真正寫一個穩(wěn)定、安全的異步程序,需要做的事情還有很多。

例如,一次異步操作結(jié)束之后會執(zhí)行一個回調(diào)函數(shù),那么如果在這個回調(diào)函數(shù)中拋出了一個異常那該怎么辦?如果不正確處理這個異常,輕則造成資源泄露,重則造成進(jìn)程退出。因此在每個回調(diào)函數(shù)中,您會發(fā)現(xiàn)try...catch塊是必不可少的——甚至還需要兩段。

更復(fù)雜的可能還是在于邏輯控制上。這樣一個數(shù)據(jù)傳輸操作很顯然需要循環(huán)——讀一段,寫一段。但是由于需要編寫成二段式的異步調(diào)用,因此程序的邏輯會被拆得七零八落,我們沒法使用一個while塊包圍整段邏輯,編寫一個異步程序本來就是那么復(fù)雜。 #p#

編寫簡單的代理

現(xiàn)在我們已經(jīng)有了一個異步傳輸數(shù)據(jù)的組件,就用它來做一些有趣的事情吧。例如,我們可以在ASP.NET應(yīng)用程序中建立一個簡單的代理,即給定一個URL,在服務(wù)器端發(fā)起這樣一個請求,并將這個URL的數(shù)據(jù)傳輸?shù)娇蛻舳藖?。簡單起見,除了進(jìn)行數(shù)據(jù)傳輸之外,我們只需要簡單地輸出Content Type頭信息即可。以下代碼僅供參考:

  1. public class AsyncWebTransfer  
  2. {  
  3.     private WebRequest m_request;  
  4.     private WebResponse m_response;  
  5.  
  6.     private HttpContextBase m_context;  
  7.     private string m_url;  
  8.  
  9.     public AsyncWebTransfer(HttpContextBase context, string url)  
  10.     {  
  11.         this.m_context = context;  
  12.         this.m_url = url;  
  13.     }  
  14.  
  15.     public void StartAsync()  
  16.     {  
  17.         this.m_request = WebRequest.Create(this.m_url);  
  18.         this.m_request.BeginGetResponse(this.EndGetResponseCallback, null);  
  19.     }  
  20.  
  21.     private void EndGetResponseCallback(IAsyncResult ar)  
  22.     {  
  23.         try  
  24.         {  
  25.             thisthis.m_response = this.m_request.EndGetResponse(ar);  
  26.             thisthis.m_context.Response.ContentType = this.m_response.ContentType;  
  27.  
  28.             var streamIn = this.m_response.GetResponseStream();  
  29.             var streamOut = this.m_context.Response.OutputStream;  
  30.  
  31.             var transfer = new AsyncTransfer(streamIn, streamOut);  
  32.             transfer.Completed += (sender, args) => this.OnCompleted(args.Error);  
  33.             transfer.StartAsync();  
  34.         }  
  35.         catch(Exception ex)  
  36.         {  
  37.             this.OnCompleted(ex);  
  38.         }  
  39.     }  
  40.  
  41.     private void OnCompleted(Exception ex)  
  42.     {  
  43.         if (this.m_response != null)  
  44.         {  
  45.             this.m_response.Close();  
  46.             this.m_response = null;  
  47.         }  
  48.  
  49.         var handler = this.Completed;  
  50.         if (handler != null)  
  51.         {  
  52.             handler(this, new CompletedEventArgs(ex));  
  53.         }  
  54.     }  
  55.  
  56.     public event EventHandler<CompletedEventArgs> Completed;  

如果說之前的AsyncTransfer類是基于“Begin/End異步編程模型”實現(xiàn)的基于事件的異步模式,那么AsyncWebTransfer便是基于“基于事件的異步模式”實現(xiàn)的基于事件的異步模式了。嗯,似乎有點繞口,不過我相信這段代碼對您來說還是不難理解的。

使用F#完成異步工作

事實上我已經(jīng)很久沒有寫過這樣的代碼了,咎其原因還是被F#給寵壞了。嘗試了C# 2.0之后我便拋棄了Java語言,熟悉了C# 3.0之后我用C# 2.0就快寫不了程序了,而使用了F#進(jìn)行異步編程之后,我就再也沒有使用C#寫過異步操作了。那么我們就來看看F#是如何進(jìn)行異步數(shù)據(jù)傳輸?shù)陌桑?/p>

  1. let rec transferAsync (streamIn: Stream) (streamOut: Stream) buffer =   
  2.     async {  
  3.         let! lengthRead = streamIn.AsyncRead(buffer, 0, buffer.Length)  
  4.         if lengthRead > 0 then  
  5.             do! streamOut.AsyncWrite(buffer, 0, lengthRead)  
  6.             do! transferAsync streamIn streamOut buffer  
  7.     } 

上面的代碼利用了尾遞歸進(jìn)行不斷地數(shù)據(jù)傳輸,我們也可以使用傳統(tǒng)的while循環(huán)來實現(xiàn)這個功能:

  1. let transferImperativelyAsync (streamIn: Stream) (streamOut: Stream) buffer =   
  2.     async {  
  3.         let hasData = ref true  
  4.         while (hasData.Value) do  
  5.             let! lengthRead = streamIn.AsyncRead(buffer, 0, buffer.Length)  
  6.             if lengthRead > 0 then  
  7.                 do! streamOut.AsyncWrite(buffer, 0, lengthRead)  
  8.             else  
  9.                 hasData :false 
  10.     } 

有了transferAsync函數(shù),編寫一個資源請求的代理也是幾分鐘的事情:

  1. let webTransferAsync (context: HttpContextBase) (url: string) =  
  2.     async {  
  3.         let request = WebRequest.Create(url)  
  4.         use! response = request.GetResponseAsync()  
  5.         context.Response.ContentType <- response.ContentType  
  6.           
  7.         let streamIn = response.GetResponseStream()  
  8.         let streamOut = context.Response.OutputStream  
  9.         let buffer = Array.zeroCreate 1024  
  10.         do! transferAsync streamIn streamOut buffer  
  11.     } 

沒錯,就是這么簡單,這就是F#中編寫異步任務(wù)方式。在執(zhí)行這兩個函數(shù)時(當(dāng)然確切地說,是執(zhí)行這兩個函數(shù)所生成的異步工作流),便會在出現(xiàn)“感嘆號”的操作之處自動分成二段式的異步調(diào)用,但是在程序的寫法上和同步代碼可謂毫無二致。 #p#

使用F#實現(xiàn)基于事件的異步模式

當(dāng)然,光有上面的代碼還不夠,因為這樣的代碼無法交給C#代碼來使用,我們還需要將它們封裝成基于事件的異步模式。不過這也非常簡單,使用一個通用的抽象基類即可:

  1. [<AbstractClass>]  
  2. type AsyncWorker(asyncWork: Async) =   
  3.       
  4.     let completed = new Event()  
  5.  
  6.     [<CLIEvent>]  
  7.     member e.Completed = completed.Publish  
  8.  
  9.     member e.StartAsync() =   
  10.         Async.StartWithContinuations  
  11.             (asyncWork,  
  12.              (fun _ -> completed.Trigger(new CompletedEventArgs(null))),  
  13.              (fun ex -> completed.Trigger(new CompletedEventArgs(ex))),  
  14.              (fun ex -> ex |> ignore)) 

在使用F#進(jìn)行面向?qū)ο箝_發(fā)時,由于不需要C#的架子代碼,它實現(xiàn)相同的結(jié)構(gòu)一般都會顯得緊湊不少(不過在我看來,C#在進(jìn)行一般的命令式編程時還是比F#來的方便一些)。在StartAsync方法中,我們使用Async.StartWithContinuations發(fā)起一個異步工作流,而這個異步工作流便是從構(gòu)造函數(shù)中傳入的具體任務(wù)。StartWithContinuations方法的后三個參數(shù)分別是成功時的回調(diào),失敗后的回調(diào),以及任務(wù)取消后的回調(diào)。您可能會說,難道F#中不需要異常處理,不需要資源釋放嗎?當(dāng)然需要。只不過:

1) 異常處理已經(jīng)由StartWithContinuations統(tǒng)一完成了,我們只要按照“同步式”代碼的寫法編寫邏輯,也就是說,從語義上說您可以看作存在一個巨大的try...catch圍繞著整段代碼。

2) 而對于資源釋放來說,您可以發(fā)現(xiàn)在webTransferAsync方法中有一個use!指令,這便是告訴F#的異步框架,在整個異步工作流結(jié)束之后需要調(diào)用這個資源的Dispose方法——沒錯,您可以把它看作是一種能在異步環(huán)境下工作的C# using關(guān)鍵字。有了AsyncWorker類之后,AsyncTransfer和WebAsyncTransfer類也可輕易實現(xiàn)了:

  1. type AsyncTransfer(streamIn: Stream, streamOut: Stream) =   
  2.     inherit AsyncWorker(  
  3.         Transfer.transferAsync streamIn streamOut (Array.zeroCreate 1024))  
  4.  
  5. type AsyncWebTransfer(context: HttpContextBase, url: string) =  
  6.     inherit AsyncWorker(Transfer.webTransferAsync context url)最后,只要在ASP.NET MVC中使用即可:  
  7.  
  8. public void LoadFsAsync(string url)  
  9. {  
  10.     AsyncManager.OutstandingOperations.Increment();  
  11.     var transfer = new FSharpAsync.AsyncWebTransfer(HttpContext, url);  
  12.  
  13.     transfer.Completed += (sender, args) => 
  14.         AsyncManager.OutstandingOperations.Decrement();  
  15.  
  16.     transfer.StartAsync();  
  17. }  
  18.  
  19. public ActionResult LoadFsCompleted()  
  20. {  
  21.     return new EmptyResult();  

事實上,在ImageController中我還提供了一個LoadAsync及對應(yīng)的LoadCompleted方法,它們使用的是利用C#實現(xiàn)的AsyncWebTransfer類。猜猜看這樣的代碼長成什么樣?其實只是將上面的AsyncWebTransfer的命名空間改成CSharpAsync而已——F#與其它.NET代碼是真正做到無縫集成的。

總結(jié)

這便是F#的偉大之處。時常有朋友會問我為什么對F#有那么大的興趣,我想,如果借助F#可以用十分之一的時間,十分之一的代碼行數(shù),寫出執(zhí)行效果相同,但可維護(hù)性高出好幾倍的程序來。

【編輯推薦】

  1. F#與ASP.NET:基于事件的異步模式與異步Action
  2. F#中的異步及并行模式:代理的高級使用
  3. F#中的異步及并行模式:反饋進(jìn)度的事件
  4. 詳解F#對象序列化為XML的實現(xiàn)方法
  5. F#中關(guān)于代理的基本使用
責(zé)任編輯:王曉東 來源: 老趙的博客
相關(guān)推薦

2010-04-06 15:20:56

ASP.NET MVC

2010-03-26 19:03:19

F#異步并行模式

2010-01-26 08:25:06

F#語法F#教程

2010-01-07 10:04:18

F#函數(shù)式編程

2010-03-26 18:31:59

F#異步并行模式

2009-08-19 09:42:34

F#并行排序算法

2010-03-16 09:09:04

F#

2010-01-15 08:33:13

F#F#類型推斷F#教程

2009-11-16 09:05:46

CodeTimer

2010-03-26 19:22:08

F#代理

2009-09-10 14:18:59

Functional F#

2009-08-13 17:25:21

F#入門

2009-08-13 17:39:48

F#數(shù)據(jù)類型Discriminat

2011-06-09 09:52:41

F#

2012-03-12 12:34:02

JavaF#

2010-03-08 09:17:13

F#異步

2010-12-21 08:53:04

Mono

2010-01-04 09:40:46

F#對象

2009-12-04 09:16:44

Visual Stud

2012-11-06 10:01:35

ContinuatioF#
點贊
收藏

51CTO技術(shù)棧公眾號