一个关于异步的纠结问题

      在博客园的一篇文章http://www.cnblogs.com/yuyijq/archive/2011/02/22/1960273.html#2033074 里面说到对于I/O密集型不需要创建一个专门的线程来执行。用异步会更合理一些。

      笔者这样认为的:ThreadPool.QueueUserWorkItem((state) => {Download();});   这样不高效,虽然Download的方法放到一个辅助线程来执行。但是这个线程在等待I/O请求时也是没有做其他事情。笔者认为用异步调用更高效。于是就不明白,为什么用异步调用更高效?

      不明白之处:下面这个是MSDN的原话:如果调用“BeginInvoke”方法,则公共语言运行库 (CLR) 将对请求进行排队并立即返回到调用方。将对来自线程池的线程调用该目标方法。提交请求的原始线程自由地继续与目标方法并行执行,该目标方法是对线程池线程运行的。
而ThreadPool.QueueUserWorkItem 在MSDN上面的解释是这样的:方法排入队列以便执行,并指定包含该方法所用数据的对象。此方法在有线程池线程变得可用时执行。
因此认为这个其实是一致的,都是让线程池的方法来执行Download方法。

      昨天下午看了下 CLR via C#里面讲的意思大概是这样的:当调用异步的BeginInvoke时,如FileStream的BeginRead是不会创建新的线程的,当程序执行到BeginInvoke的时候,会向I/O设备读取发送一个IRP的读操作(里面包含回调的委托),在设备接收到这个读操作的时候就会开始进行数据的读取(假设没有队列等待的情况下),这样程序继续执行。在设备读取完毕之后,会将这个回调委托加入到线程池的等待队列中,由线程池的线程来执行。

      相比于QueueUserWorkItem,这时由线程池的一个线程来执行,他帮我们发起I/O请求,在I/O请求的过程中这个线程进行等待。等I/O请求执行完毕之后继续处理。那么在执行I/O请求的过程中,这个线程是什么事情也没有做,干等,这样的效率显然没有上面的高。

      感觉是这样才解释得通。但是又有新的问题。对于ClR via C#,里面将的是异步请求时,传送的是包含回调的为托,如果是这样的呢:

      IAsyncResult result=fileStream.BeginRead(byteArray, 0, (int)byteArray.Length, null, null);
      fileStream.EndRead(result);也就是没有传递委托的情况下,异步的执行又是怎么样的?

       猜想:还是不会创建新线程,在设备读完之后是通知调用BeginRead的程序,让其继续执行。这样似乎也解释得通,有出现了一个新的问题,这也就意味着I/O异步和下面的异步调用同步方法是有区别的。

View Code
1 AsyncMethodCaller caller = new AsyncMethodCaller(TestMethod);
2 IAsyncResult result = caller.BeginInvoke(3000, out threadId, null, null);
3 string returnValue = caller.EndInvoke(out threadId, result);

      这个是可以确定会新创建一个线程(线程池中的线程)来执行TestMethod的。

      待验证的结论:异步调用同步方法有区别于I/O异步,前者会创建新的线程后者不会,因此异步调用同步也是适用于CPU密集型。

      问题:怎么来验证这个结论?用Reflector反编译I/O异步的方法找不到答案?怎么验证?

      再续:对于FileStream.BeginRead()会不会创建新的线程还是不确定,但是经过查找资料后得知,Net ThreadPool类中的线程分为Worker Thread和I/O Thread。而Worker Thread可以看成.Net通过Thread类预先创建的一组线程。.Net及ThreadPool类中提供的方法,如QueueUserWorkItem, Timer, delegate回调等使用的都是Worker Thread。而.Net中对I/O操作的封装,如FileStream, NetworkStream等则是使用的IO Thread。IO Thread是对I/O专门提供的,效率更高些!

posted @ 2011-02-25 11:10  雁北飞  阅读(299)  评论(0编辑  收藏  举报