技术开发 频道

.NET异步编程:IO完成端口与BeginRead

  那么如果创建FileStream的时候指定了异步会是什么结果呢(这里的实现在BeginReadCore方法里)?

[SecuritySafeCritical]
private unsafe FileStreamAsyncResult BeginReadCore(byte[] bytes, int offset, int numBytes, AsyncCallback userCallback, object stateObject, int numBufferedBytesRead)
{
    NativeOverlapped
* overlappedPtr;
    FileStreamAsyncResult ar
= new FileStreamAsyncResult {
        _handle
= this._handle,
        _userCallback
= userCallback,
        _userStateObject
= stateObject,
        _isWrite
= false,
        _numBufferedBytes
= numBufferedBytesRead
    };
    ManualResetEvent event2
= new ManualResetEvent(false);
    ar._waitHandle
= event2;
    Overlapped overlapped
= new Overlapped(0, 0, IntPtr.Zero, ar);
    
//...
    overlappedPtr = overlapped.Pack(IOCallback, bytes);
    
//...
    ar._overlapped = overlappedPtr;

    
//...
    ReadFileNative(this._handle, bytes, offset, numBytes, overlappedPtr, out hr)

  上面代码中的NativeOverlapped就是在上一节我们提到的保存有回调等信息的OVERLAPPED结构,在这里也是一样,它保存有我们的userCallback回调。然后通过调用ReadNative发起IO请求,并将这个数据结构传递进去,这里的ReadNative就是对Win32 的ReadFile的封装。发起异步IO请求完毕,BeginRead返回,过了一会儿磁盘驱动程序将数据读回来了,对应的IO完成端口收到通知,IO完成端口把刚才传递进去的NativeOverlapped结构传递给IO线程,IO线程从中取出IOCallback回调,IOCallback回调里有对我们的userCallback回调的调用:

IOCallback = new IOCompletionCallback(FileStream.AsyncFSCallback);
private static unsafe void AsyncFSCallback(uint errorCode, uint numBytes, NativeOverlapped* pOverlapped)
{
    FileStreamAsyncResult asyncResult
= (FileStreamAsyncResult) Overlapped.Unpack(pOverlapped).AsyncResult;
    
//...
    AsyncCallback callback = asyncResult._userCallback;
    
if (callback != null)
    {
        callback(asyncResult);
    }
}

  在这个回调里我们会对EndRead进行调用,我们看看EndRead的代码会发现其他一些东西:

public override unsafe int EndRead(IAsyncResult asyncResult)
{
    
//...
    WaitHandle handle = result._waitHandle;
    
if (handle != null)
    {
        
try
        {
            handle.WaitOne();
        }
        
finally
        {
            handle.Close();
        }
    }
    NativeOverlapped
* nativeOverlappedPtr = result._overlapped;
    
if (nativeOverlappedPtr != null)
    {
        Overlapped.Free(nativeOverlappedPtr);
    }
    
//...
    return (result._numBytes + result._numBufferedBytes);
}

  首先是销毁我们在BeginRead里初始化的WaitHandle内核对象,然后将NativeOverlapped结构也销毁。所以EndRead除了取回读了多少个字节的作用外,还起了销毁资源的作用。所以有的时候我们想进行这么一个操作:异步的发起请求,但是我们并不关心该请求是否成功。如是我们就假想能不能只调用BeginXXX方法就可以了?从这里看我们不能简单的调用一下BeginXXX就了事了,因为在BeginXXX里分配的一些句柄和内核资源需要在EndXXX里销毁,不然会造成资源泄露。

1
相关文章