我正在写一个服务器,当收到请求时,我将每个动作发送到一个单独的线程。我这样做是因为几乎每个请求都要进行数据库查询。我正在使用线程池库来减少线程的构造/破坏。

我的问题是:对于这样的I/O线程,什么是一个好的截断点?我知道这只是一个粗略的估计,但我们说的是几百吗?成千上万的吗?

我怎么才能算出这个界限呢?


编辑:

感谢大家的回复,似乎我只是要测试一下,以找出我的线程数上限。问题是:我怎么知道我已经达到上限了呢?我到底应该测量什么?


当前回答

有些人会说两个线程太多了-我不属于那个阵营:-)

我的建议是:衡量,不要猜测。一个建议是将其设置为可配置,并最初将其设置为100,然后将软件发布到野外并监视发生的情况。

如果线程使用的峰值是3,那么100就太多了。如果一天的大部分时间都保持在100,那就把它提高到200,看看会发生什么。

实际上,您可以让您的代码本身监视使用情况,并在下次启动时调整配置,但这可能是多余的。


为了澄清和阐述:

我不提倡滚动您自己的线程池子系统,务必使用您现有的线程池子系统。但是,由于您正在询问线程的良好分界点,我假设您的线程池实现能够限制创建的线程的最大数量(这是一件好事)。

我已经编写了线程和数据库连接池代码,它们具有以下特性(我认为这对性能至关重要):

活动线程的最小数量。 线程的最大数目。 关闭一段时间没有使用的线程。

第一个设置线程池客户机的最低性能基线(这个线程数量总是可用的)。第二个设置活动线程使用资源的限制。第三种是在安静的时候让你回到基线,以尽量减少资源的使用。

您需要平衡拥有未使用线程的资源使用(A)和没有足够线程执行工作的资源使用(B)。

(A)通常是内存使用情况(堆栈等),因为不做任何工作的线程不会使用太多CPU。(B)通常会延迟处理请求,因为他们到达,因为你需要等待一个线程变得可用。

这就是为什么要测量。在此过程中,绝大多数线程将等待数据库的响应,因此它们不会运行。有两个因素影响您应该允许多少线程。

第一个是可用的DB连接数量。这可能是一个硬限制,除非您可以在DBMS中增加它——我将假设您的DBMS在这种情况下可以接受无限数量的连接(尽管您也应该理想地测量它)。

那么,线程的数量应该取决于历史使用情况。您应该运行的最小值是您曾经运行过的最小值+ A%,具有绝对最小值(例如,并使其像A一样可配置)5。

最大线程数应该是历史最大值+ B%。

您还应该监视行为变化。如果由于某种原因,您的使用率在很长一段时间内达到了可用容量的100%(因此会影响客户机的性能),那么您应该提高允许的最大值,直到它再次高B%。


在回答“我究竟应该测量什么?”的问题时:

您应该具体测量的是负载下并发使用的线程的最大数量(例如,等待DB调用的返回)。然后添加一个10%的安全系数(强调一下,因为其他海报似乎把我的例子作为固定的建议)。

此外,这应该在生产环境中进行调优。事先进行估计是可以的,但您永远不知道生产过程中会出现什么情况(这就是为什么所有这些东西都应该在运行时可配置)。这是为了捕捉一种情况,例如意想不到的双倍客户端调用。

其他回答

在大多数情况下,您应该允许线程池处理这个问题。如果您发布一些代码或提供更多细节,可能更容易看到是否有某些原因导致线程池的默认行为不是最好的。

你可以在这里找到更多关于如何工作的信息:http://en.wikipedia.org/wiki/Thread_pool_pattern

需要考虑的一件事是,将执行代码的机器上有多少内核。这表示在任何给定时间内可以处理多少线程的硬限制。但是,如果像您的情况一样,线程需要频繁地等待数据库执行查询,那么您可能需要根据数据库可以处理多少并发查询来调优线程。

这个问题已经讨论得很透彻了,我还没有机会阅读所有的回答。但是,在查看可以在给定系统中和平共存的并发线程数量的上限时,有几件事需要考虑。

Thread Stack Size : In Linux the default thread stack size is 8MB (you can use ulimit -a to find it out). Max Virtual memory that a given OS variant supports. Linux Kernel 2.4 supports a memory address space of 2 GB. with Kernel 2.6 , I a bit bigger (3GB ) [1] shows the calculations for the max number of threads per given Max VM Supported. For 2.4 it turns out to be about 255 threads. for 2.6 the number is a bit larger. What kindda kernel scheduler you have . Comparing Linux 2.4 kernel scheduler with 2.6 , the later gives you a O(1) scheduling with no dependence upon number of tasks existing in a system while first one is more of a O(n). So also the SMP Capabilities of the kernel schedule also play a good role in max number of sustainable threads in a system.

现在,您可以调整堆栈大小以合并更多线程,但随后必须考虑线程管理的开销(创建/销毁和调度)。 您可以对给定进程和给定线程强制CPU Affinity,将它们绑定到特定的CPU上,以避免CPU之间的线程迁移开销,并避免冷现金问题。

请注意,一个人可以根据自己的意愿创建数千个线程,但是当Linux耗尽VM时,它只是随机地开始杀死进程(因此线程)。这是为了防止实用程序配置文件被刷爆。(效用函数表示给定资源数量的系统范围效用。在这种情况下,CPU周期和内存的资源不变,效用曲线会随着任务数量的增加而变平)。

我相信windows内核调度器也会做一些这样的事情来处理资源的过度使用

[1] http://adywicaksono.wordpress.com/2007/07/10/i-can-not-create-more-than-255-threads-on-linux-what-is-the-solutions/

我已经编写了大量多线程应用程序。我通常允许由配置文件指定潜在线程的数量。当我针对特定客户进行调优时,我将数值设置得足够高,以至于所有CPU核心的利用率都相当高,但还没有高到遇到内存问题(当时这些是32位操作系统)。

换句话说,一旦你达到某个瓶颈,比如CPU、数据库吞吐量、磁盘吞吐量等,添加更多的线程不会提高整体性能。但在达到这一点之前,请添加更多线程!

请注意,这是假设系统是专门为你的应用程序,你不需要很好地发挥(避免饥饿)其他应用程序。

你应该记住的一件事是,python(至少是基于C的版本)使用了所谓的全局解释器锁,它可以对多核机器的性能产生巨大影响。

如果你真的很需要多线程python,你可能会考虑使用Jython或其他东西。