使用IAsyncResult设计模式的异步操作是通过名为Begin操作名称和操作名称End的两个方法来实现的,这两个方法分别开始和结束异步操作。在调用Begin操作名称后,应用程序可以继续在调用线程上执行指令,同时异步操作在另一个线程上执行。每次调用Begin操作名称时,应用程序还应调用End操作名称来获取操作的结果。
Begin 操作名称方法立即将控制权返回给调用线程。如果Begin操作名称方法引发异常,则会在开始异步操作之前引发异常。如果Begin操作名称方法引发异常,则意味着没有调用回调方法。
End操作名称方法可结束异步操作。End操作名称方法的返回值与其同步副本的返回值类型相同,并且是特定于异步操作的。从应用程序的主线程调用End操作名称方法,阻止应用程序执行,直到操作完成之后再继续执行。
1 IAsyncResult ar=异步对象。BeginXXX(null,null);//控制权返回当前线程,继续执行 2 do something 3 异步对象。EndXXX(ar);//等待异步执行完毕,并处理完成继续后面操作 4 do otherthing
由此可见End操作名称方法的正确实现应该使用返回的IAsyncResult对象的WaitHandle,或者使用IsCompleted轮询来等待异步执行完成并处理。为了验证,我使用Reflector查看了FileStream的EndRead方法。
1 public override unsafe int EndRead(IAsyncResult asyncResult) 2 { 3 if (asyncResult == null) 4 { 5 throw new ArgumentNullException("asyncResult"); 6 } 7 if (!this._isAsync) 8 { 9 return base.EndRead(asyncResult); 10 } 11 FileStreamAsyncResult result = asyncResult as FileStreamAsyncResult; 12 if ((result == null) || result._isWrite) 13 { 14 __Error.WrongAsyncResult(); 15 } 16 if (1 == Interlocked.CompareExchange(ref result._EndXxxCalled, 1, 0)) 17 { 18 __Error.EndReadCalledTwice(); 19 } 20 WaitHandle handle = result._waitHandle; 21 if (handle != null) 22 { 23 try 24 { 25 handle.WaitOne(); 26 } 27 finally 28 { 29 handle.Close(); 30 } 31 } 32 NativeOverlapped* nativeOverlappedPtr = result._overlapped; 33 if (nativeOverlappedPtr != null) 34 { 35 Overlapped.Free(nativeOverlappedPtr); 36 } 37 if (result._errorCode != 0) 38 { 39 __Error.WinIOError(result._errorCode, Path.GetFileName(this._fileName)); 40 } 41 return (result._numBytes + result._numBufferedBytes); 42 }
在异步编程中使用End操作方法是必需的,即使End操作方法不返回任何值(可能为产生内存泄露,或者其它未知异常)。有一种简单的使用异步的编程方法必须谨慎,在Begin操作方法中使用AsyncCallback(调用End操作方法),这时虽然不需要再去调用End操作方法,代码运行也畅通无阻,但是有可能根本来不及执行AsyncCallback程序已经结束。
另外如果在Begin操作方法和End操作方法之间如果无otherthing可做,或者剩下的工作必须等待End操作方法完成之后才可以继续,此时不建议使用异步(没啥意义)。