如何为 "await DoSthAsync" 安排线程

How a thread is scheduled for "await DoSthAsync"

using System;
using System.IO;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

public class Program
{
  public static void Main(string[] args)
  {
    Read(File.OpenText("test.txt")); // content: "read from file"
    Read(new StreamReader(new MemoryStream(
      Encoding.UTF8.GetBytes("read from string")));
    Console.WriteLine("{0}: {1}", 
      Thread.CurrentThread.ManagedThreadId, "main thread");
    Console.ReadKey();
  }
  static async Task Read(StreamReader sr)
  {
    var s = await sr.ReadToEndAsync();
    Console.WriteLine("{0}: {1}", 
      Thread.CurrentThread.ManagedThreadId, s);
  }
}

输出:

3: read from file

1: read from string

1: main thread

以上,为什么 read from file 生成了一个新线程而不是 read from string

更具体地说,为什么异步文件IO需要一个新的线程来完成?

它只是底层同步文件 IO 的包装版本,然后通过生成新线程进行分派吗?

Why does read from file spawn a new thread but not read from string?

让我在这里澄清一下,这些调用都没有产生新线程——事实上,没有创建新线程,或者正如我的朋友 Stephen Cleary 喜欢说的"there is no thread".

Why does the asynchronous file IO require a new thread to complete?

现在,线程标识符不同的原因是这些调用之一实际上使文件保持打开状态。我创建了一个 .NET Fiddle that demonstrates that regardless of correctly waiting for the async calls to complete the thread id still changes. The clue is buried in the source code of the StreamReader. The .ctor(string path) ultimately passes in true on the leaveOpen parameter, whereas the .ctor(Stream stream) passes false for that parameter. Look at the two constructor paths here.

引用上述文章: 由于 library/BCL 使用标准的 P/Invoke 重叠 I/O 系统,它已经向 I/O 完成端口(IOCP)注册了句柄,它是线程池的一部分.所以暂时借用了一个I/O线程池线程来执行APC,通知任务完成。

更新

我又 运行 了一遍,这就是结果 .NET Fiddle:

这似乎告诉我的一件事是,因为实际上写入文件系统的两个是真正的 I/O 绑定操作——而另一个只是在内存中。不确定这是否有助于为您澄清事情?