距離微軟發布Async CTP已經有個把月了吧,周圍大家都在熱議著Async。如果你對Async已經非常熟悉,那麼,請直接略過…如果你跟我一樣,只會一點點非同步編程,但又覺得以前的非同步編程比較麻煩,那麼,讓我們一起來探索下一代的C#會為我們帶來什麼。 (Async CTP同樣對VB有支援的。)
##本文的範例是基於Async CTP SP1 Refresh#完成。由於 Async還處於CTP#階段,很多東西還在討論,因此,也許待到C# 5.0 發布的時候,細節還會改變。但是,大體的思路,概念應該是不會有什麼改變了。
進入正題:首先,要試試Async功能,我們需要安裝Visual Studio 2010 SP1和Microsoft Visual Studio Async CTP (SP1 Refresh)。
我們先設定一個簡單的任務,分別來看一下,同步編程,利用回呼進步非同步編程和Async程式設計的方法,然後來透過他們來分析一下,Async到底是什麼,它為我們帶來了什麼。
任務:建立一個Windows Form應用程序,當點擊按鈕時,先顯示一行字,例如,開始計算什麼的,用以表示狀態,然後計算從1#到int.Max/2 #的累加,並把結果顯示出來。
同步我們會這麼做:首先,寫一個函數來實作基本演算法:private void btnSync_Click( object sender, EventArgs e) { lblResult.Text = "Start to do something . . ." ; long value = DoSomething( int .MaxValue / 2); lblResult.Text = value.ToString(); } |
代码第一行改写Label的字样;第二行调用算法获得结果;第三行把结果输出。看似挺不算的。运行一下,就会发现有两个问题:
这个算法需要四五秒钟左右的实现时间,并且在这几秒钟的时间里,界面是锁死的,也就是说应用程序就像死了一样,它不接受任何用户操作。(也许我的电脑比较差,呵呵,所以,如果你没有遇到这种情况,请加大输入参数的值,让它算一会儿。)
我们没有看到Start to do something这一行字。
OK,出现这个现象也是可以理解的,因为我们把大量的运算添加到了UI线程里面了。所以,解决方法就是把它放到外面。我试了一下不用Async,实现的代码如下:
private void 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之后呢?呵呵,代码说明一切:
public Task < long > DoSomethingAsync( int n) { return TaskEx .Run< long >(() => DoSomething(n)); } private async void 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#。
我們來仔細看程式碼:
#首先,我把要非同步執行的程式碼的回傳值寫成 Task8742468051c85b06f0a0af9e3e506b5c。這個回傳值其實有三個選項:void,Task和##Task8742468051c85b06f0a0af9e3e506b5c#,具體怎麼用,大家查MSDN上C#4.0#中的#Task 類別吧。
然後,我呼叫了TaskEx#中的Run4db15037c2d45d75b28ec2b6a696f099方法,傳遞給它一個回傳值為long的方法--就是我們的任務的演算法啦。
如果你有興趣研究,可以看一下Run8742468051c85b06f0a0af9e3e506b5c其實調用了Task.Factory. StartNew8742468051c85b06f0a0af9e3e506b5c,而這個Start48c15ee73f6e332a8a171e10c6563c6c則是先建了一個Task#,然後呼叫了它的Start方法…
好,把演算法封成任務部分完成。 第二部分程式碼比較容易解釋了:第一行改寫Label的字樣;第二行調用演算法獲得結果;第三行把結果輸出。 <--本行複製/#貼上自前文:-)
呵呵,讓我們來看看更細一點,比較一下Sync和Async的程式碼:
#Sync | ##非同步 |
## private# #void##private## #void # btnSync_Click(object 寄送者, |
}
private async #void## btn#sync_Click(## #void## btn#1 ##物件發送者, |
}
首先,我們在方法名稱上加上async修飾,宣告這是一個有非同步呼叫的方法;
然後,我們在返回Task8742468051c85b06f0a0af9e3e506b5c的函數呼叫(##DoSomethingAsync#)之前添加一個await關鍵字。來猜猜看x是什麼類型的?答案是long型。有了await之後,即使在設計時,編譯器會自動把Task8742468051c85b06f0a0af9e3e506b5c的類型,轉換成T類型。
程式碼到這裡結束了,但是,新的Async功能為我們帶來了什麼?是非同步程式設計的能力嗎?我們用Callback同樣可以實作異步,而IAsyncCallback介面應該在.NET 1.1中已經實作了;多執行緒的命名空間也早就存在;Task在C# 4.0#中被引入…
我想,Async給程式設計師帶來的是一種程式碼邏輯為中心並且實作多線程編程的方式。透過最後的比較,我們看到,Async的程式碼與#Sync的程式碼相差無幾,程式不再需要花費大量精力去考慮回調、同步等等的問題…這與C#一直在努力的方向是一致的,程式設計師更多的來描述是什麼而不是怎麼做。
最後,附上應用程式下載和原始程式碼,還有運行介面截圖…(好吧,我不是美工,請原諒:-) )
點擊下載原始碼#點擊Async,看到運行提示:
## http://msdn.microsoft.com/Async
以上就是C# 5.0功能之Async一瞥的圖文程式碼詳解的內容,更多相關內容請關注PHP中文網(www.php.cn)!
#