【问题标题】:Async IO without completion port?没有完成端口的异步 IO?
【发布时间】:2018-05-22 07:33:42
【问题描述】:
// 1- 
using(FileStream file = File.Open("ConfusedDev", FileMode.Open)) {
    await file.ReadAsync(buffer, 0, 1024);
    Thread.Sleep(Timeout.Infinite); // threadpool shows 1 worker thread in use
}

// 2- 
using(FileStream file = new FileStream("ConfusedDev", FileMode.Open, FileAccess.Read, FileShare.Read, 1024, FileOptions.Asynchronous)) {
    await file.ReadAsync(buffer, 0, 1024);
    Thread.Sleep(Timeout.Infinite); // threadpool shows 1 async IO thread in use
}
  • 可以肯定地说 case1 等同于Task.Run(() => file.Read) 吗?也就是说,线程池中的一个线程之前被阻塞了 读取返回,而 case2 没有阻塞线程,如中所述 这篇文章:"There is no thread"
  • 什么时候用case1(好像是默认的方式引入 Microsoft Doc) 在 case2 上。我在服务器端做一些工作,case2 可能会给我更多的空闲线程来处理传入的请求?
  • 这只发生在文件上吗?我对httpClient().GetAsync() 进行了测试,它默认使用异步 IO 线程,但也许有 GetAsync() 分离另一个线程的实现?

【问题讨论】:

    标签: c# asynchronous threadpool iocp


    【解决方案1】:

    您的大部分问题似乎只需查看源代码即可得到解答:

    • 可以肯定地说 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),事实上像 HttpClientNetworkStream 是建立在 Socket 类之上的,它没有这样的选项,并且异步操作将通过该类的异步 I/O 实现,它总是使用 IOCP。

    所以,不……您不会在 HttpClient 类中找到一个选项,该选项禁止使用 IOCP 进行异步操作。

    当然,您始终可以自己包装同步调用,以使用主线程池而不是 IOCP 线程池,这样的行为就像对非异步 FileStream 对象的异步调用一样。

    【讨论】:

      猜你喜欢
      • 2014-02-20
      • 1970-01-01
      • 2014-02-08
      • 1970-01-01
      • 2021-08-05
      • 1970-01-01
      • 1970-01-01
      • 2012-03-16
      • 2021-08-30
      相关资源
      最近更新 更多