您的大部分问题似乎只需查看源代码即可得到解答:
• 可以肯定地说 case1 等同于 Task.Run(() => file.Read)?换句话说,线程池中的一个线程在读取返回之前被阻塞,而 case2 没有阻塞线程,如这篇文章所述:“没有线程”。
File.OpenRead() 不会将FileOptions.Asynchronous 传递给FileStream 构造函数,因此任何异步调用都是使用线程池中的阻塞I/O 实现的。具体来说,对ReadAsync() 的调用最终会调用FileStream.BeginRead(),如果实例不是使用FileOptions.Asynchronous 创建的,它会将读取委托给the base class BeginRead(), which eventually executes this anonymous method as a task:
delegate
{
// The ReadWriteTask stores all of the parameters to pass to Read.
// As we're currently inside of it, we can get the current task
// and grab the parameters from it.
var thisTask = Task.InternalCurrent as ReadWriteTask;
Contract.Assert(thisTask != null, "Inside ReadWriteTask, InternalCurrent should be the ReadWriteTask");
// Do the Read and return the number of bytes read
var bytesRead = thisTask._stream.Read(thisTask._buffer, thisTask._offset, thisTask._count);
thisTask.ClearBeginState(); // just to help alleviate some memory pressure
return bytesRead;
}
虽然我完全同意“没有线程”的文章,但重要的是要避免过于字面意思。 IOCP 比将线程专用于单个操作更有效,但它仍然涉及 一些 个线程。只是可以使用更小的线程池,并且任何给定的线程都能够响应更多操作的完成。
• 何时使用 case1(似乎是 Microsoft Doc 引入的默认方式)而不是 case2。我在服务器端做一些工作,case2 可能会给我更多的空闲线程来处理传入的请求?
这个问题实在是太宽泛了,而且主要是基于意见的。但我认为您应该始终对任何重要的文件 I/O 使用 FileOptions.Asynchronous。如果出于某种原因您决定放弃它并使用File.OpenRead(),那么您不应该费心使用任何异步调用。
File.OpenRead() 对于执行非常简单的同步 I/O 的短程序来说很方便。但是,如果您打算随后调用FileStream 对象(例如ReadAsync()、BeginRead() 等)上的异步方法,则应该永远使用File.OpenRead()。如果代码以异步方式运行足够重要,那么确保它使用 Windows 中的高效异步功能实际执行也足够重要。
• 这只发生在文件上吗?我对 httpClient().GetAsync() 进行了测试,它默认使用异步 IO 线程,但也许有 GetAsync() 分拆另一个线程的实现?
显然,您要询问的行为特定于代码示例中显示的唯一区别:使用File.Open()(不通过FileOptions.Asynchronous)和使用FileStream 构造函数和@987654340 @ 选项。因此,询问这是否仅发生在文件上并没有什么意义。 File.Open() 方法和 FileStream 对象根据定义仅适用于文件。
也就是说,如果您要找到一个具有类似选项(即是否启用异步 I/O)的不同类,它肯定会以相同的方式工作(即在未启用该选项的情况下不使用 IOCP),事实上像 HttpClient 或 NetworkStream 是建立在 Socket 类之上的,它没有这样的选项,并且异步操作将通过该类的异步 I/O 实现,它总是使用 IOCP。
所以,不……您不会在 HttpClient 类中找到一个选项,该选项禁止使用 IOCP 进行异步操作。
当然,您始终可以自己包装同步调用,以使用主线程池而不是 IOCP 线程池,这样的行为就像对非异步 FileStream 对象的异步调用一样。