c# - ThreadPool 线程执行 I/O 操作 - 线程可以在等待时重用吗?

标签 c# .net multithreading async-await threadpool

我一直在阅读有关 async/await 与 ThreadPool 与 Threads 的一些内容,我不得不承认,我对细节并不完全清楚。有一个具体问题我想我有答案,但我不能肯定地说。

我也知道关于 SO 和其他地方的许多问题,这些问题正在被讨论和解释。我在这里阅读了一些关于 SO 的内容,但我还没有找到明确的答案,或者至少没有我想要的那么清楚。

我收集到的:

  • 在 I/O 操作中使用 async/await 将使该线程可用于其他事情,而 I/O 操作在别处继续
  • 对于服务器应用程序,这意味着更高的吞吐量等

  • 然而,一位同事是这样说的:
  • 使用 ThreadPool 执行相同的 I/O 操作几乎与 async/await 的行为相同
  • 当 ThreadPool 线程将 I/O 操作“移交”给操作系统时,它可能会等待答案,但这无关紧要,因为 CPU 上没有工作(线程正在等待),因此,ThreadPool/OS/framework 可以使用或产生另一个线程来做一些其他的工作。
  • 由于线程池有大约 32000 多个线程的限制,所以没有太大关系,因为它可以使用更多线程。一个线程的开销很小,只有几个字节的内存

  • 所以,我想问的是:
  • 来自 ThreadPool 的线程是否会阻塞 I/O 操作?
  • 如果这样做是否重要,因为如果需要其他工作,操作系统/框架/线程池可以只使用池中的另一个线程?
  • 底线:使用 async/await 或 ThreadPool 进行 I/O 操作;效率和吞吐量重要吗?

  • 请注意,我不是在讨论客户端的事情,只是从服务器的角度来看。

    如果我错过了对此的确切答案,请提前抱歉,我已经看过了 =)

    最佳答案

    真正的问题不是“线程池与 async/await”,而是同步 I/O 与异步 I/O。 [1]

    在 Windows 上,线程是相对昂贵的对象——一个线程有很多与之相关的操作系统簿记,更不用说 1 MB 的预分配堆栈空间。这对系统甚至支持的线程数量设置了相当低的上限,即使您没有达到该上限,所有这些线程之间的上下文切换也不便宜。 “32,000 个线程”的限制是一个严格的理论限制,并且非常看好您可以拥有多少线程并且仍然可以响应! [2]

    进入异步 I/O,它被优化为仅使用尽可能多的线程(通常是系统中物理处理器内核数量的一些保守倍数),理想情况下甚至不创建超出初始批处理的新线程。这些线程专用于处理已完成的 I/O 操作,方法是将它们从队列(称为完成端口)中移除。当异步操作正在进行时,根本没有线程专用于它,甚至没有作为等待列表中的项目(Stephen Cleary 有一个很好的 blog post 对此进行了更详细的解释)。思考什么更有效不需要太多想象力:

  • 几千个单独的线程,每个线程都等待一个特定的操作,根据完成的操作,这些线程必须被唤醒并切换到(和之间);或
  • 几十个线程(如果有的话),每个线程都可以处理任何已完成的操作,因此只需要根据需要运行尽可能多的线程来响应。

  • 事实证明,后者的扩展性比前者好得多;即使在您使用线程池来减少新线程的创建时,在原始服务器代码中常见的“每个请求的线程”模型也很快显示出其局限性。请注意,这是一个早在 async 之前的问题。/await曾经是一回事,Windows 所采用的解决方案也是如此; async/await只是一种使用现有机制编写代码的新方法。

    您是否可能会注意到一次只有少数几个请求的差异?没有。但是因为 async/await本质上允许您编写看起来同步但具有“免费”异步 I/O 可扩展性的代码,为什么不选择使用它来支持排队到线程池的同步 I/O?

    [1] 事实证明斯蒂芬·克利里已经 wrote most of what's in this answer几年前。我建议你也读一下。

    [2] 这是一个 older post作者:Mark Russinovich,他实际上试图从系统中挤出尽可能多的线程——只是为了好玩和盈利。在所有资源都消失之前,他“只”在 64 位机器上达到 55K,这就是调整默认堆栈大小,而没有做任何实际有用的工作。在现代系统上,您可能会得到更多,但真正的问题不应该是“我可以拥有多少个线程”——如果是,那么您就做错了。

    关于c# - ThreadPool 线程执行 I/O 操作 - 线程可以在等待时重用吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/56425062/

    相关文章:

    .net - 从 VB6.0 升级的最佳开发工具

    c# - 代码优先的实体约束 "check"

    multithreading - 好的多线程设计是否会过早优化?

    multithreading - QProgressBar 不显示进度?

    c# - 构造函数内部或外部的成员初始化

    C# 随机方法导致 "hidden dash"

    c# - Azure 媒体服务声音

    c# - Windows Mobile 在应用程序关闭时释放资源(.net 3.5 cf)

    java - Android:我的异步方法有效吗?

    c# - Visual Studio 17.8,我没有看到 .NET 8.0 作为 Blazor Server 项目模板的选项