C# 5.0功能之Async一瞥

距離微軟發佈Async CTP已經有幾個月了吧,周圍大家都在熱議着Async。如果你對Async已經非常熟悉,那麼,請直接略過……如果你跟我一樣,只會一點點異步編程,但又覺得以前的異步編程比較麻煩,那麼,讓我們一起來探索一下下一代的C#會給我們帶來什麼。(Async CTP同樣對VB有支持的。)

本文的例子基於Async CTP SP1 Refresh完成。由於 Async還處於CTP階段,很多東西還在討論,因此,也許待到C# 5.0發佈的時候,細節還會變動。但是,大體的思路,概念應該是不會有什麼變化了。

進入正題:

首先,要試用Async功能,我們需要安裝Visual Studio 2010 SP1Microsoft Visual Studio Async CTP (SP1 Refresh)

我們首先設定一個簡單的任務,分別來看一下,同步編程,利用回調進步異步編程和Async編程的方法,然後來通過他們來分析一下,Async到底是什麼,它給我們帶來了什麼。

任務:

建立一個Windows Form應用程序,當點擊按鈕時,先顯示一行字,例如,開始計算什麼的,用以表示狀態,然後計算從1int.Max/2的累加,並把結果顯示出來。

同步我們會這麼做:

首先,寫一個函數來實現基本算法:

        #region Do things
                    
publiclong DoSomething(int n)
                     {
                        
long result = 1;
                        
for (int i = 1; i <= n; i++)
                         {
                             result += i;
                         }
                        
return result;
                     }
        #endregion

然後,添加一個按鈕的Click事件處理程序:

       privatevoid btnSync_Click(object sender,EventArgs e)
                     {
                         lblResult.Text =
"Start to do something . . .";
                        
long value = DoSomething(int.MaxValue / 2);
                         lblResult.Text = value.ToString();
                     }

代碼第一行改寫Label的字樣;第二行調用算法獲得結果;第三行把結果輸出。看似挺不算的。運行一下,就會發現有兩個問題:

  1. 這個算法需要四五秒鐘左右的實現時間,並且在這幾秒鐘的時間裏,界面是鎖死的,也就是說應用程序就像死了一樣,它不接受任何用戶操作。(也許我的電腦比較差,呵呵,所以,如果你沒有遇到這種情況,請加大輸入參數的值,讓它算一會兒。)
  1. 我們沒有看到Start to do something這一行字。

OK,出現這個現象也是可以理解的,因爲我們把大量的運算添加到了UI線程裏面了。所以,解決方法就是把它放到外面。我試了一下不用Async,實現的代碼如下:

       privatevoid btnCallback_Click(object sender,EventArgs e)
                     {
                         lblResult.Text =
"Start to do something . . .";
                         Func<
int,long> callBackDelegate =this.DoSomething;
            callBackDelegate.BeginInvoke(
                            
int.MaxValue / 2,
                            
new AsyncCallback(
                             a =>
                             {
                                 lblResult.Invoke(
new MethodInvoker(() =>
                                     {
                                         lblResult.Text = callBackDelegate.EndInvoke(a).ToString();
                                     }));
                             }),
                            
null);
                     }

如果你覺得這段代碼比較暈,那就跳過這一節吧。可能我代碼寫得不好,大家將就看我簡單解釋一下,我首先給DoSomething寫了一個代理,然後,調用了代理的BeginInvoke方法,把算法放到了其它的Thread中去調用了。這個代理執行完了以後,因爲它不會直接返回一個long型的值,而是會去執行一個AsyncCallBack,所以,就在這個Callback裏,去調用這個代理的EndInvoke()

好吧,且不論代碼質量,這個就是有Async之前的一種實現異步的方法。

從這個代碼裏,我們完全看不到原來代碼的影子,我也沒有辦法像解釋同步代碼一樣解釋:第一、第二、第三……有了Async之後呢?呵呵,代碼說明一切:

       publicTask<long> DoSomethingAsync(int n)
                     {
                       
returnTaskEx.Run<long>(() => DoSomething(n));
                     }

        privateasyncvoid btnAsync_Click(object sender,EventArgs e)
                     {
                         lblResult.Text =
"Start do something...";
                        
var x =await DoSomethingAsync(int.MaxValue / 2);
                         lblResult.Text = x.ToString();
                     }

三件事:

第一:添加文件引用:AsyncCtpLibrary.dll。相信Async正式發佈之後,這個會出現在.NET應用程序集裏。

第二:把DoSomething封裝成一個Task

第三:添加一些關鍵字,例如async,例如await

我們來仔細看一下代碼:

首先,我把要異步執行的代碼的返回值寫成Task<T>。這個返回值其實有三個選項:voidTaskTask<T>,具體怎麼用,大家查MSDNC#4.0中的Task類吧。

然後,我調用了TaskEx中的Run<long>方法,傳遞給它一個返回值爲long的方法——就是我們的任務的算法啦。

如果你有興趣研究,可以看一下Run<T>其實調用了Task.Factory.StartNew<T>,而這個Start<New>則是先建了一個Task,然後調用了它的Start方法……

好,把算法封成任務部分完成。

第二部分代碼比較容易解釋了:

第一行改寫Label的字樣;第二行調用算法獲得結果;第三行把結果輸出。<--本行復制/粘貼自前文:-)

呵呵,讓我們看看細一點,比較一下SyncAsync的代碼:

Sync

Async

       privatevoid btnSync_Click(object sender,EventArgs e)
                     {
                         lblResult.Text =
"Start to do something . . .";
                        
long value = DoSomething(int.MaxValue / 2);
                         lblResult.Text = value.ToString();
                     }

       privateasync void btnAsync_Click(object sender,EventArgs e)
                     {
                         lblResult.Text =
"Start do something...";
                        
var x =await DoSomethingAsync(int.MaxValue / 2);
                         lblResult.Text = x.ToString();
                     }

首先,我們在方法名上加上async修飾,聲明這是一個有異步調用的方法;

然後,我們在返回Task<T>的函數調用(DoSomethingAsync)之前添加一個await關鍵字。來猜猜看x是什麼類型的?答案是long型。有了await之後,即使在設計時,編譯器會自動把Task<T>的類型,轉換成T類型。

代碼到這裏結束了,但是,新的Async功能給我們帶來了什麼?是異步編程的能力嗎?我們用Callback同樣可以實現異步,而IAsyncCallback接口應該在.NET 1.1中已經實現了;多線程的命名空間也早就存在;TaskC# 4.0中被引入……


我想,Async給程序員帶來的是一種代碼邏輯爲中心並且實現多線程編程的方式。通過最後的比較,我們看到,Async的代碼與Sync的代碼相差無幾,程序不再需要花大量精力去考慮回調、同步等等的問題……這與C#一直在努力的方向是一致的,程序員更多的來描述是什麼而不是怎麼做。

最後,附上應用程序下載和源代碼,還有運行界面截圖……(好吧,我不是美工,請原諒 :-)

點擊下載源代碼

點擊Async,看到運行提示:


顯示執行結果:

最新最官方的Async資料在這兒:^v^

http://msdn.microsoft.com/Async

Little knowledge is dangerous.

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章