执行I/O操作的ThreadPool线程-等待时可以重用线程吗? [英] ThreadPool thread doing I/O operation - can thread be reused while waiting?

查看:91
本文介绍了执行I/O操作的ThreadPool线程-等待时可以重用线程吗?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我已经阅读了一些有关异步/等待vs线程池vs线程的知识,我不得不承认,我不清楚细节.我有一个特定的问题,我认为,但我不能肯定地说.

我也知道关于SO和其他地方的许多问题,这些问题正在讨论和解释中.在这里,我已经读到了很少的文章,但是我没有找到明确的答案,或者至少没有我想要的清晰.

我收集了什么

  • 在I/O操作中使用异步/等待,将使该线程可用于其他用途,而I/O操作将在其他地方继续
  • 对于服务器应用程序,这意味着更高的吞吐量等

但是,一位同事说了这样的话:

  • 使用ThreadPool执行相同的I/O操作的行为与async/await几乎相同
  • 当ThreadPool线程将I/O操作移交给"操作系统时,它可能会等待答案,但这无关紧要,因为在CPU上没有完成任何工作(该线程是等待),因此ThreadPool/OS/framework可以使用或产生另一个线程来执行其他一些工作.
  • 由于线程池最多可以容纳32000多个线程,因此没关系,因为它可以使用更多线程.线程的开销很小,只有几个字节的内存

所以,我要问的是:

  • I/O操作中的ThreadPool块中的线程是否存在?
  • 是否有关系,因为OS/framework/Threadpool可以在需要其他工作时仅使用池中的另一个线程吗?
  • 底线:使用async/await或ThreadPool的I/O操作;对效率和生产量有影响吗?

请注意,我并不是从服务器的角度讨论客户端的事情.

如果对这个问题没有确切的答案,请提前抱歉,我已经看过=)

解决方案

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

在Windows上,线程是相对昂贵的对象-线程具有很多与之相关的操作系统记账,更不用说1 MB的预分配堆栈空间了.这对系统甚至支持多少线程设置了一个较低的上限,即使您没有达到该上限,在所有这些线程之间进行上下文切换也不是一件容易的事. "32,000个线程"的限制是严格的理论限制,并且对您可以拥有多少个线程并且仍然保持响应状态非常乐观! [2]

输入异步I/O,该异步I/O经过优化以仅使用所需的多个线程(通常是系统中物理处理器内核数量的保守倍数),理想情况下甚至从不创建超出初始批处理的新线程.这些线程专用于通过从队列(称为完成端口)中删除它们来处理已完成的I/O操作.在进行异步操作时,根本没有线程专用于此,甚至没有作为等待列表中的项使用(Stephen Cleary的async/await成为现实之前就存在问题,因此Windows使用的解决方案也是如此. async/await只是一种编写代码以使用现有机制的新方法.

您是否可能一次注意到只有几个请求而有所不同?不会.但是,由于async/await本质上允许您编写看起来像同步但具有免费"的异步I/O可伸缩性的代码,所以为什么选择在支持同步I/O排队到线程池?


[1]证明Stephen Cleary已经写了这个答案的大部分内容几年前.我也建议您阅读.

[2]这是一个旧帖子,他实际上试图从系统中挤出尽可能多的线程-只是为了娱乐和牟利.在所有资源用尽之前,他只能"在64位计算机上达到55K,这是通过调整默认堆栈大小来完成的,而没有做任何实际有用的工作.在现代系统上,您可能会得到更多,但真正的问题不应该是我可以拥有多少个线程"-如果是,那您做错了.

I have been reading a bit about async/await vs ThreadPool vs Threads, and I have to admit, I'm not fully clear on the details. There is one specific question that I think I have the answer to, but I can't say for sure.

I am also aware of the many many questions on SO and elsewhere, where these questiosn are being discussed and explained. I have read a fair few here on SO, but I haven't found a clear answer, or at least as clear as I'd like.

What I gathered:

  • Using async/await in an I/O operation, will make that thread available to use for other things, while the I/O operation continues elsewhere
  • For a server application, this means higher throughput etc

However, a collegue said something like this:

  • that using a ThreadPool to execute the same I/O operation would behave almost the same as async/await
  • that when the ThreadPool thread "hands over" the I/O operation to the OS, it might wait for the answer, but that it won't matter much, since there is no work done on the CPU (the thread is waiting), and thus, the ThreadPool/OS/framework can use or spawn another thread to do some other work.
  • Since the threadpool has a limit of some 32000+ threads, it wouldn't matter much, since it could just use more threads. The cost for a thread is very small, just a few bytes of memory

So, what I am asking is:

  • Does the Thread from a ThreadPool block in I/O operations?
  • Does it matter if it does, since the OS/framework/Threadpool can just use another thread from the pool if needed for some other work?
  • Bottom line: I/O operations with async/await or ThreadPool; does it matter for efficiency and throughput?

Please note that I am not discussing the client-side of things, just from a server perspective.

And sorry in advance if I missed an exact answer to this, I have looked =)

解决方案

The real issue is not "thread pool vs. async/await", it's synchronous I/O vs. asynchronous I/O. [1]

On Windows, threads are relatively expensive objects -- a thread has a lot of OS bookkeeping associated with it, not to mention 1 MB of preallocated stack space. This puts a fairly low cap on how many threads the system will even support, and even when you don't hit that cap, context switching between all those threads is not cheap either. That "32,000 threads" limit is a strictly theoretical one, and highly optimistic about how many threads you can have and still be responsive! [2]

Enter asynchronous I/O, which is optimized to use only as many threads as necessary (usually some conservative multiple of the number of physical processor cores in the system), ideally never even creating new threads beyond the initial batch. These threads are dedicated to handling completed I/O operations by removing them from a queue (known as a completion port). While an asynchronous operation is in progress, no thread is dedicated to it at all, not even as an item on a wait list (Stephen Cleary has a nice blog post about it that explains this in more detail). Not much imagination is needed to think about what's more efficient:

  • A few thousand individual threads that each wait on a particular operation, which have to be woken up and switched to (and between) depending on what operation(s) completed; or
  • A few dozen threads (if that), each of which can handle any completed operation, so that only as many ever need to run as necessary to be responsive.

As it turns out, the latter scales much better than the former; the "thread per request" model which is common in naive server code quickly shows its limits, even when you use a thread pool to reduce the creation of new threads. Note that this was an issue long before async/await was ever a thing, and so is the solution Windows went with; async/await is just a new way of writing code to use the existing mechanisms.

Are you likely to notice a difference with only a few requests in flight at a time? No. But since async/await essentially allows you to write code that looks synchronous but has the scalability of asynchronous I/O "for free", why would you not choose to use that in favor of synchronous I/O queued to the thread pool?


[1] Turns out Stephen Cleary already wrote most of what's in this answer a few years ago. I recommend you read that as well.

[2] Here's an older post by Mark Russinovich where he actually tries to squeeze as many threads out of the system as possible -- just for fun and profit. He "only" gets to 55K on a 64-bit machine before all resources are gone, and that's with adjusting the default stack size, and without doing any actually useful work. On a modern system you could probably get more, but the real question should not be "how many threads can I have" -- if it is, you're Doing it Wrong.

这篇关于执行I/O操作的ThreadPool线程-等待时可以重用线程吗?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆