關于IAsyncResult接口的CompletedSynchronously屬性
IAsyncResult大伙都用的多吧,在異步編程的時候,我們通常根據它的IsCompleted屬性來判斷異步操作是否已完成。不過該接口還有另外一個屬性CompletedSynchronously,MSDN的解釋一如既往的簡單(查閱MSDN的時候,往往能看懂它的文字,不理解它的意思,有同感的朋友舉手。):獲取異步操作是否同步完成的指示。
異步操作,同步完成,到底啥意思呢。假如我是IAsyncResult接口的實現者,我該如何設置何時設置CompletedSynchronously的值呢?假如我是IAsyncResult接口實現類的使用者,CompletedSynchronously對于我來說又有什么用呢?以下是我個人看法,若有錯誤,歡迎拍磚。
假設我們自定義的類需要實現某個耗費時間可能長可能短的功能,時間長短并不受代碼控制,比如獲取某個資源進行操作,請求資源時經常需要排隊,偶爾又能馬上取得。
1 public class CompletedSynchronouslyShow
2 {
3 int _blockInterval = new Random().Next(0, 10000);
4
5 /// <summary>
6 /// 為了更好地說明問題,我們將該方法定義成有返回值
7 /// </summary>
8 /// <returns>執行是否成功</returns>
9 public bool DoSth()
10 {
11 //模擬真實情形,阻塞不定時間
12 System.Threading.Thread.Sleep(_blockInterval);
13
14 //其它操作
15 return true;
16 }
17 }
為了不阻塞當前線程,我們需要使用異步方式比如new Thread(Show.DoSth).Start()來開啟新線程調用DoSth(Show為CompletedSynchronouslyShow類型的對象)。眾所周知,雖然硬件性能步步高升,但開啟并維護一個新線程對系統來說還是一件體力活。如果阻塞的時間并不久,我們希望仍然在當前線程執行DoSth方法。由于阻塞時間只能在DoSth方法內部做判斷,因此上述愿望的實現不能期望調用方,不論異步還是同步,調用方調用的只能是同一個方法。既然是同一個方法,那么方法的返回值不能是原先的返回值(這里是布爾型),一般情況下IAsyncResult是最合適的(我們也可以自定義返回類型,只要能滿足要求)。而IAsyncResult接口的CompletedSynchronously屬性正是指示方法執行完成時所在的線程是否就是調用線程。
下面是修改后的代碼:
1 public class CompletedSynchronouslyShow
2 {
3 int _blockInterval = new Random().Next(0, 10000);
4
5 /// <summary>
6 /// 為了更好地說明問題,我們將該方法定義成有返回值
7 /// </summary>
8 /// <returns>執行是否成功</returns>
9 private bool DoSth()
10 {
11 //模擬真實情形,阻塞不定時間
12 System.Threading.Thread.Sleep(_blockInterval);
13
14 //其它操作
15 return true;
16 }
17
18 //客戶端調用該方法而不是DoSth方法
19 public IAsyncResult BeginDoSth(AsyncCallback callback = null, object state = null)
20 {
21 return new AsyncResult(_blockInterval, new Func<bool>(DoSth), callback, state);
22 }
23 }
24
25 public class AsyncResult : IAsyncResult
26 {
27 private Func<bool> _doSth;
28 private AsyncCallback _callback;
29
30 public bool Result { get; private set; }
31
32 private bool _completedSynchronously;
33 public bool CompletedSynchronously
34 {
35 get { return _completedSynchronously; }
36 }
37
38 private bool _isCompleted;
39 public bool IsCompleted
40 {
41 get { return _isCompleted; }
42 }
43
44 private object _state;
45 public object AsyncState
46 {
47 get { return _state; }
48 }
49
50 public AsyncResult(int blockInterval, Func<bool> doSth, AsyncCallback callback, object state)
51 {
52 _doSth = doSth;
53 _state = state;
54 _callback = callback;
55 //小于3秒直接返回值
56 if (blockInterval < 3000)
57 {
58 Result = doSth();
59 //_completedSynchronously設為true表示同步執行
60 this._completedSynchronously = this._isCompleted = true;
61 if (_callback != null)
62 {
63 _callback(this);
64 }
65 }
66 else
67 {
68 doSth.BeginInvoke(new AsyncCallback(SetResult), null);
69 70 }
71 }
72
73 private void SetResult(IAsyncResult ar)
74 {
75 Result = _doSth.EndInvoke(ar);
76 if (_callback != null)
77 {
78 _callback(this);
79 }this._isCompleted = true;
80 }
81 }
恩,少年們于是又不淡定了:這不是玩我呢吧,這都啥玩意。上述代碼核心在于實現了IAsyncResult接口,通過它我們才能展開后續工作。這里說明CompletedSynchronously屬性的意義:BeginDoSth方法給人一種異步的錯覺,但它同時兼有異步和同步的特性,由CompletedSynchronously屬性區分。它還體現了一種編程模式,該模式在.Net框架中很多地方都在使用,比如WCF輸出信道接口IOutputChannel:
1 public interface IOutputChannel
2 {
3 IAsyncResult BeginSend(Message message, AsyncCallback callback, object state);
4 void EndSend(IAsyncResult result);
5 }
很多具有異步操作的類型都有類似BeginXX和EndXX成對出現的方法,參數也大同小異。EndXX的作用同委托的EndInvoke方法一樣,可供回調函數使用,當我們需要獲取操作的返回值時,在回調函數中一般要調用該方法。
作為IAsyncResult接口實現方,CompletedSynchronously一般設為false即可,一個原因是判斷何時采用同步方式合適采用異步方式(如例子中獲取阻塞時間)在大多數情況下并不容易。我覺得在資源請求、Stream操作等方面可以考慮這個問題。網絡通信中,Socket.BeginSendTo方法就可能返回CompletedSynchronously為true的對象,它是按照什么來作為設置的依據,我反射了代碼,也查不出一個所以然,我只好猜測它是根據發送字節總數和單次最大發送字節數比較結果選擇是否發送方式(同步or異步)。
有時候CompletedSynchronously屬性似乎并不像它本身意思,更多的是“數據是否完整”(多次異步操作數據流會引出這個概念)。我是看Async Programming Model - Reloaded一文得出這個結論的,該文說道web響應流BeginRead返回對象若CompletedSynchronously為true,則說明web響應流已經全部接收完畢,此時應該使用Read同步方法。我覺得該說法有誤,既然BeginRead返回的CompletedSynchronously已經為true,說明BeginRead執行在當前線程,那么后續執行也應該是當前線程,從這么方面來說它和Read方法是一樣的,可能在其它方面會有損耗(比如構造IAsyncResult對象)。另一方面,若真如該文所說,那么即使某次BeginRead讀取的字節已經是接收到本地的,但由于數據并未全部接收完,所以該次BeginRead返回的CompletedSynchronously仍為false。不論如何,真相只有一個,留待以后驗證,有知道的朋友希望不吝賜教,感激不盡。
轉載請注明本文出處:http://www.rzrgm.cn/newton/archive/2012/11/30/2795614.html

浙公網安備 33010602011771號