技术开发 频道

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

  BeginRead&EndRead

  那么,既然有IO完成端口这么个好东西,如是有很多人想在.NET里也利用利用。其实大可不必,在.NET里异步的IO内部就是使用了IO完成端口。每个CLR初始化后都会创建一个IO完成端口,用来处理IO请求。很多人应该知道ThreadPool里的线程分为两类:worker thread和io completion thread,这里的io completion thread就是上一节说的跟IO完成端口相关联的那些thread。要说它跟其他的thread有什么不同?没什么不同,只是受IO完成端口控制而已。

  为了看看在.NET中是如何利用IO完成端口的,我们将FileStream.BeginRead作为我们的入口点。在FileStream的Init方法里我们会看到这么一段代码:

if (this._isAsync)
{
    
//...
    try
    {
        flag4
= ThreadPool.BindHandle(this._handle);
    }
    
finally
    {
        CodeAccessPermission.RevertAssert();
    }
    
//...
}

  我们感兴趣的就是ThreadPool.BindHandle。还记得上面对IO完成端口的描述么?其实这里做的事儿就是将该文件句柄与每个CLR都初始化了的那个IO完成端口绑定。也就是说如果我们创建一个FileStream时指定了异步,那么IO完成端口就会“监视”这个文件。

  我们再来看看BeginRead这个方法。该方法是用来发起异步IO请求的方法,该方法执行后会立即返回,不阻塞线程。

  首先,看这么段代码:

if (!this._isAsync)
{
    
return base.BeginRead(array, offset, numBytes, userCallback, stateObject);
}

  也就是说如果我们创建FileStream时,没有指定为异步,就会调用基类的BeginRead方法,那基类的这个方法又是如何实现的呢?

[HostProtection(SecurityAction.LinkDemand, ExternalThreading=true)]
public virtual IAsyncResult BeginRead(byte[] buffer, int offset, int count, AsyncCallback callback, object state)
{
    
//...
    ReadDelegate delegate2 = new ReadDelegate(this.Read);
    
//...
    return delegate2.BeginInvoke(buffer, offset, count, callback, state);
}

  其实是创建一个调用同步的Read方法的委托,然后调用一下BeginInvoke方法(在第二篇文章已经说过,这样的调用实际上还是让线程池里的一个线程来调用,我们可以称之为一种伪异步IO)。这里可以得出一个结论:如果你想用BeginRead,那么初始化FileStream的时候就指定异步,否则就不用直接用Read。

1
相关文章