有多少线程太多了?

rye*_*guy 291 performance multithreading threadpool

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

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


编辑:

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

pax*_*blo 188

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

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

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

可能确实有你的代码本身监控使用情况和调整配置下一次启动,但是这可能是矫枉过正.


澄清和阐述:

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

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

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

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

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

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

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

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

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

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

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


回应"我应该测量什么?" 题:

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

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


Jay*_*y D 35

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

  1. 线程堆栈大小:在Linux中,默认线程堆栈大小为8MB(您可以使用ulimit -a来查找它).
  2. 给定OS变体支持的最大虚拟内存.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功能也在系统中可持续线程的最大数量方面发挥了很好的作用.

现在,您可以调整堆栈大小以包含更多线程,但是您必须考虑线程管理(创建/销毁和调度)的开销.您可以对给定进程以及给定线程强制执行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/

  • 请注意,这些虚拟内存限制仅适用于 32 位系统。在 64 位上,您不会耗尽虚拟内存。 (2认同)

And*_*ant 16

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

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

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

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

  • 他/是/在游泳池中跑步. (2认同)
  • @Andrew:为什么?它应该在每次收到请求时向线程池添加一个任务.当有一个线程可用时,由线程池为任务分配线程. (2认同)

Cha*_*ere 9

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

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

  • 读完之后,我尝试在三个线程上运行Eratosthenes任务的筛子.果然,它实际上比在单个线程中运行相同的任务慢50%*.谢谢你的提醒.我在一个分配了两个CPU的虚拟机上运行Eclipse Pydev.接下来,我将尝试一个涉及一些数据库调用的场景. (3认同)
  • 有两种(至少)类型的任务:CPU绑定(例如图像处理)和I/O绑定(例如从网络下载).显然,GIL"问题"不会过多地影响I/O绑定任务.如果您的任务是CPU绑定的,那么您应该考虑多处理而不是多线程. (3认同)

bor*_*yer 7

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

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

Combien defilsd'exécution的完整措施(仅限法语).

  • 我认为,如果你有15000个线程阻塞某些外部I/O,那么一个更好的解决方案是大大减少线程,但使用异步模型.我是根据这里的经验说的. (17认同)
  • 15,000?这也比我的预期要高一点。不过,如果这就是你得到的,那就是你得到的,我无法否认这一点。 (2认同)
  • 对于此特定应用程序,大多数线程只是等待来自DNS服务器的响应.因此,在挂钟时间内,并行性越高越好. (2认同)

Mat*_*und 5

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

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

请注意,这假设相关系统专用于您的应用,并且您不必表现出色(避免饥饿)其他应用。

  • 你能提到一些你见过的线程数吗?了解它会很有帮助。谢谢。 (3认同)

Hot*_*cks 5

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

如果不可能,请考虑您拥有可替代资源(CPU)和不可替代资源(arm)。对于 CPU,将每个线程分配给特定的 CPU 并不重要(尽管这有助于缓存管理),但对于 Arm,如果无法将线程分配给 Arm,则会陷入排队论以及保留 Arm 的最佳数量忙碌的。一般来说,我认为如果您无法根据所使用的臂来路由请求,那么每个臂有 2-3 个线程就差不多了。

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

当然,您必须权衡所有这些与线程的“重量”。不幸的是,大多数系统都有非常重量级的线程(他们所说的“轻量级线程”通常根本不是线程),所以最好在低端犯错。

我在实践中看到的是,非常细微的差异可能会对最佳线程数量产生巨大影响。特别是,缓存问题和锁冲突会极大地限制实际并发量。