文章目錄
- 為什麼要自己實現APM
- PowerThreading類庫中的AsyncResult和AsyncResult<T>
Note: 本篇主要內容來自Jeffery Richard的Implementing the CLR Asynchronous Programming Model,看過的同學可以略過。
在前一篇中介紹了使用APM開發多線程程式的有點,同時關於如何使用APM方式的文章也很多了。所以,這篇主要想看看如何使用Jeffery Richard的Power Threading類庫,來開發一個支援APM的類。
為什麼要自己實現APM
前一篇中,提到了受計算約束(Computing-Bound)與受I/O約束(I/O-Bound)的區別。如果我們是一個受計算約束類型的任務,完全可以將這個方法變成委託(Delegate)的形式,然後通過BeginInvoke/EndInvoke來實現APM;另外,FCL本身也封裝了大量對I/O非同步作業的類,如FileStream,NetworkStream等,那我們為什麼還要自己實現APM呢。Jeffery Richard列舉了4個原因:
- 儘管FCL提供很多與裝置通訊的非同步類,但仍有部分未提供,如並口;或者FCL提供的功能不夠
- 對現有的I/O類庫添加功能,如我們在WebHttpRequest可以開發一個HTTP的過濾類等
- 實現受計算約束的APM
- 對不支援非同步模式裝置提供非同步訪問方式
PowerThreading類庫中的AsyncResult和AsyncResult<T>
APM的核心就是IAsyncResult介面。當調用類非同步方式BeginXxx時,返回IAsyncResult的對象。為了返回滿足這個介面的對象,我們的類需要維護的一個 是否完成的狀態IsCompleted;一個ManuelResetEvent對象及一個委託保持回呼函數。這些雖然比較簡單,但很麻煩。使用PowerThreading類庫中AsyncResult和AsyncResult<T>,我們可以非常容易的實現支援APM的類。
AsyncResult用於沒有傳回值,而AsyncResult<T>用於傳回型別為T的任務。下面的例子中,我們看看如何使用AsyncResult<T>。
首先,我們準備實現一個能夠過濾Stream對象的類,類定義如下:
1 class StreamFilter
2 {
3 private Stream m_stream;
4 public StreamFilter(Stream stream)
5 {
6 }
7
8 public IAsyncResult BeginParse(
9 AsyncCallback callback, Object state)
10 {
11 }
12
13 public Stream Parse()
14 {
15 }
16
17 public Stream EndParse(IAsyncResult asyncResult)
18 {
19 }
20 }
Parse()是同步提供者,執行主要的任務,而BeginParse()和EndParse()是對應的非同步介面。下面是使用AsyncResult<T>後的代碼
1 class StreamFilter
2 {
3 private Stream m_stream;
4 public StreamFilter(Stream stream)
5 {
6 m_stream = stream;
7 }
8
9 public IAsyncResult BeginParse(
10 AsyncCallback callback, Object state)
11 {
12 //建立一個IAsyncResult對象來標誌非同步作業
13 AsyncResult<Stream> ar =
14 new AsyncResult<Stream>(callback, state);
15
16 // 調用輔助函數,並傳遞AsycnResult對象
17 // 如果使用Anonymouse Method更簡潔
18 ThreadPool.QueueUserWorkItem(ParseHelper,ar);
19
20 return ar; // 返回IAsyncResult
21 }
22
23 private void ParseHelper(Object asyncResult)
24 {
25 var ar = (AsyncResult<Stream>)asyncResult;
26 try
27 {
28 // 執行真正的任務
29 Stream result = Parse();
30
31 // 更新AsycnResult狀態
32 ar.SetAsCompleted(result, false);
33 } catch (Exception e)
34 {
35 // 儲存Exception對象
36 ar.SetAsCompleted(e, false);
37 }
38 }
39
40 public Stream Parse()
41 {
42 // 分析m_stream,並返回過濾後的stream對象
43 Stream filterStream = null;
44 return filterStream;
45 }
46
47 public Stream EndParse(IAsyncResult asyncResult)
48 {
49 var ar = (AsyncResult<Stream>)asyncResult;
50 // 等待任務完成
51 return ar.EndInvoke();
52 }
53 }
比較重要的是ParseHelper函數,這個函數在新的線程中執行,通過ar.SetAsCompleted來更新狀態。 如果結合匿名方法或者Limbda運算式,代碼可以變得更加的緊湊。
參考:
Implementing the CLR Asynchronous Programming Model by Jeffery Richard
Power Threading類庫