有多少线程太多了?

时间:2009-01-27 00:46:06

标签: multithreading performance threadpool

我正在编写服务器,并在请求传入时将每个操作分支到一个线程中。我这样做是因为几乎每个请求都进行数据库查询。我正在使用线程池库来减少线程的构造/销毁。

我的问题是 - 对于像这样的I / O线程来说,什么是一个很好的截止点?我知道这只是一个粗略的估计,但我们是在说几百个?数千?


编辑:

谢谢大家的回复,似乎我只是要测试它以找出我的线程数上限。问题是:我怎么知道我达到了这个上限?究竟应该测量什么?

12 个答案:

答案 0 :(得分:180)

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

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

如果你的线程使用率达到3,那么100就太多了。如果它在一天中的大部分时间保持在100,则将其提高到200,看看会发生什么。

可以实际上让你的代码本身监控使用情况,并在下次启动时调整配置,但这可能有点过分。


澄清和阐述:

我不是主张滚动你自己的线程池子系统,一定要使用你拥有的那个。但是,既然您在询问线程的良好截止点,我认为您的线程池实现能够限制创建的最大线程数(这是一件好事)。

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

  • 最少活动线程数。
  • 最大线程数。
  • 关闭一段时间没用过的线程。

第一个根据线程池客户端设置最低性能的基线(此线程数始终可用)。第二个设置了活动线程对资源使用的限制。第三个会在安静时间返回基线,以便最大限度地减少资源使用。

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

(A)通常是内存使用(堆栈等),因为不执行任何工作的线程不会占用大量CPU。 (B)在您需要等待线程可用时,通常会延迟处理请求。

这就是你测量的原因。正如您所说,绝大多数线程将等待数据库的响应,因此它们将不会运行。有两个因素会影响您应该允许的线程数。

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

然后,您应该拥有的线程数取决于您的历史用途。您应该运行的最小值是您运行的最小数量+ A%,绝对最小值(例如,并使其可配置为A)5。

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

您还应该监控行为更改。如果由于某种原因,您的使用时间达到可用时间的100%(这会影响客户的性能),您应该提高允许的最大值,直到它再次高出B%。


回应“我应该测量什么?”问题:

您应该具体衡量的是在负载下并发使用的最大线程数(例如,等待DB调用返回)。然后为示例添加10%的安全系数(强调,因为其他海报似乎将我的示例视为固定推荐)。

此外,这应该在生产环境中进行调整。可以事先得到一个估计,但你永远不知道什么样的产品会引发你的方式(这就是为什么所有这些东西都应该在运行时配置)。这是为了捕捉到一些情况,例如客户来电的意外加倍。

答案 1 :(得分:34)

这个问题已经过彻底讨论,我没有机会阅读所有回复。但是,在考虑在给定系统中可以和平共存的同时线程数量的上限时,需要考虑的几件事情。

  1. 线程堆栈大小:在Linux中,默认线程堆栈大小为8MB(您可以使用ulimit -a来查找它)。
  2. 给定操作系统变体支持的最大虚拟内存。 Linux Kernel 2.4支持2 GB的内存地址空间。使用内核2.6,我有点大(3GB)
  3. [1]显示了每个给定Max VM Supported的最大线程数的计算。对于2.4,它结果是大约255个线程。 2.6的数字有点大。
  4. 你有什么kindda内核调度程序。将Linux 2.4内核调度程序与2.6进行比较,后者为您提供O(1)调度,不依赖于系统中存在的任务数量,而第一个调度更多的是O(n)。因此,内核调度的SMP功能也在系统中可持续线程的最大数量方面发挥了很好的作用。
  5. 现在您可以调整堆栈大小以包含更多线程,但是您必须考虑线程管理(创建/销毁和调度)的开销。 您可以对给定进程以及给定线程强制执行CPU关联,以将它们绑定到特定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/

答案 2 :(得分:16)

如果您的线程正在执行任何类型的资源密集型工作(CPU /磁盘),那么您很少会看到超过一两个的好处,并且太多会很快消除性能。

“最佳情况”是您的后续线程将在第一个线程完成时停止,或者某些线程将在具有低争用的资源上具有低开销块。最糟糕的情况是你开始颠倒缓存/磁盘/网络,你的整体吞吐量会下降。

一个好的解决方案是将请求放在池中,然后从线程池调度到工作线程(是的,避免连续的线程创建/销毁是一个很好的第一步)。

然后,可以根据分析结果,运行的硬件以及计算机上可能发生的其他事情,调整和缩放此池中活动线程的数量。

答案 3 :(得分:9)

你应该记住的一件事是python(至少是基于C的版本)使用所谓的global interpreter lock,这会对多核机器的性能产生巨大影响。

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

答案 4 :(得分:7)

正如Pax正确地说,衡量,不要猜测。我为DNSwitness做的事情和结果令人惊讶:理想的线程数比我想象的要高得多,就像15,000个线程一样,可以获得最快的结果。

当然,这取决于很多事情,这就是你必须衡量自己的原因。

Combien de fils d'exécution ?中的完整措施(仅限法语)。

答案 5 :(得分:4)

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

换句话说,一旦遇到一些瓶颈,无论是CPU,数据库吞吐量,磁盘吞吐量等,添加更多线程都不会提高整体性能。但是直到你达到这一点,添加更多线程!

请注意,这假设有问题的系统专用于您的应用,并且您不必非常好地(避免挨饿)其他应用。

答案 6 :(得分:3)

“大铁”的答案通常是每个有限资源一个线程 - 处理器(CPU绑定),arm(I / O绑定)等 - 但这只有在你可以将工作路由到正确的线程时才有效要访问的资源。

如果不可能,请考虑您拥有可替代资源(CPU)和不可替代的资源(武器)。对于CPU而言,将每个线程分配给特定的CPU并不重要(尽管它有助于缓存管理),但是对于武器,如果你不能将一个线程分配到手臂,你会进入排队理论以及保持武器的最佳数量忙。一般来说,我认为如果你不能根据所使用的手臂路由请求,那么每个手臂有2-3个线程将是正确的。

当传递给线程的工作单元没有执行合理的原子工作单元时,会出现复杂情况。例如,您可以让线程在一个点上访问磁盘,在另一个点上等待网络。这增加了额外线程可以进入并执行有用工作的“裂缝”数量,但它也增加了额外线程污染彼此的缓存等的机会,并使系统陷入困境。

当然,你必须权衡所有这一点与线程的“重量”。不幸的是,大多数系统都有非常重量级的线程(他们称之为“轻量级线程”通常根本就不是线程),因此最好在低端错误。

我在实践中看到的是,非常微妙的差异可以在多少线程最优化方面产生巨大差异。特别是,缓存问题和锁定冲突可能会极大地限制实际并发数量。

答案 7 :(得分:2)

要考虑的一件事是机器上将存在多少个将执行代码的核心。这代表了在任何给定时间可以进行多少线程的硬性限制。但是,如果在某些情况下,线程需要经常等待数据库执行查询,那么您可能希望根据数据库可以处理的并发查询数来调整线程。

答案 8 :(得分:2)

我认为这对你的问题有点躲闪,但为什么不把它们分成流程呢?我对网络的理解(从昔日的朦胧日子开始,我根本就没有真正的网络编码)是每个传入的连接可以作为一个单独的过程处理,因为如果有人在你的过程中做了一些讨厌的事情,它就不会核对整个计划。

答案 9 :(得分:1)

ryeguy,我目前正在开发一个类似的应用程序,我的线程数设置为15.不幸的是,如果我将其增加到20,它就会崩溃。所以,是的,我认为处理此问题的最佳方法是衡量当前配置是否允许多于或少于X个线程。

答案 10 :(得分:-6)

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

您可以在此处找到有关此方法的更多信息:http://en.wikipedia.org/wiki/Thread_pool_pattern

答案 11 :(得分:-10)

与CPU内核一样多的线程是我经常听到的。