代码之家  ›  专栏  ›  技术社区  ›  ryeguy

多少线程太多?[关闭]

  •  273
  • ryeguy  · 技术社区  · 16 年前

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

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


    编辑:

    谢谢大家的回答,看来我要测试一下才能找出我的线数上限。但问题是:我怎么知道我达到了那个上限?我到底应该测量什么?

    12 回复  |  直到 7 年前
        1
  •  177
  •   paxdiablo    11 年前

    有人会这么说 线程太多了-我不太在那个阵营中:—)

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

    如果您的线程使用率达到峰值3,那么100就太多了。如果一天中的大部分时间它保持在100,将它增加到200,看看会发生什么。

    能够 实际上,让您的代码本身监视使用情况,并在下次启动时调整配置,但这可能会造成过度杀伤力。


    澄清和阐述:

    我并不主张滚动您自己的线程池子系统,而是使用您拥有的那个。但是,由于您在询问线程的一个好的截止点,我假设您的线程池实现能够限制创建的线程的最大数量(这是一件好事)。

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

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

    第一个设置线程池客户机最低性能的基线(线程的数量始终可用)。第二个设置活动线程对资源使用的限制。第三种方法是在安静的时间返回到基线,以最小化资源使用。

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

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

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

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

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

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

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


    为了回应“我到底应该测量什么?”问题:

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

    此外,这应该在生产环境中进行,以便进行调整。预先得到一个估计值是可以的,但是你永远不知道生产会给你带来什么影响(这就是为什么所有这些东西都应该在运行时进行配置)。这是为了捕捉这样一种情况,比如客户机调用的意外加倍。

        2
  •  32
  •   Jay D    14 年前

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

    1. 线程堆栈大小:在Linux中,默认线程堆栈大小为8MB(可以使用ulimit-a来查找)。
    2. 给定OS变体支持的最大虚拟内存。Linux内核2.4支持2 GB的内存地址空间。对于内核2.6,我有点大(3GB)
    3. [1]显示每个支持的最大虚拟机的最大线程数的计算。对于2.4,结果是大约有255个线程。对于2.6,这个数字要大一点。
    4. 你有什么样的内核调度器?与Linux2.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/

        3
  •  16
  •   Andrew Grant    16 年前

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

    “最佳情况”是,当第一个线程完成时,稍后的线程将暂停,或者某些线程在争用较少的资源上具有较低的开销块。最坏的情况是,您开始破坏缓存/磁盘/网络,并且您的总吞吐量会下降。

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

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

        4
  •  9
  •   Chad Okere    15 年前

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

    如果您真的需要最充分的多线程Python,那么您可能需要考虑使用Jython或其他什么工具。

        5
  •  7
  •   bortzmeyer    16 年前

    正如帕克斯所说, 测量,不要猜测 . 我所做的一切 DNSwitness 结果令人吃惊:理想的线程数量比我想象的要多,比如15000个线程可以获得最快的结果。

    当然,这取决于很多事情,所以你必须测量自己。

    完成测量(仅法语) Combien de fils d'exécution ? .

        6
  •  4
  •   Matthew Lund    13 年前

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

    换言之,一旦遇到一些瓶颈(如CPU、数据库吞吐量、磁盘吞吐量等),添加更多线程不会提高整体性能。但是,在你达到这一点之前,添加更多的线程!

    请注意,这假设所讨论的系统专用于您的应用程序,并且您不必很好地播放(避免饥饿)其他应用程序。

        7
  •  3
  •   Hot Licks    13 年前

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

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

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

    当然,你必须把这些和线的“重量”进行权衡。不幸的是,大多数系统都有非常重的线程(他们称之为“轻量级线程”的线程通常根本不是线程),所以最好是在低端出错。

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

        8
  •  2
  •   newdayrising    16 年前

    要考虑的一件事是要执行代码的机器上有多少个内核。它表示在任何给定时间可以处理多少线程的硬限制。但是,如果像在您的例子中一样,线程经常等待数据库执行查询,那么您可能需要根据数据库可以处理的并发查询数来调整线程。

        9
  •  2
  •   mmr    16 年前

    我认为这有点回避你的问题,但为什么不把它们分为几个过程呢?我对网络的理解(从过去的迷茫日子开始,我根本就不编码网络)是,每个传入的连接都可以作为一个单独的过程来处理,因为如果有人在你的过程中做了一些令人讨厌的事情,它不会核化整个程序。

        10
  •  1
  •   hyperboreean    16 年前

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

        11
  •  -6
  •   GEOCHET S.Lott    16 年前

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

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

        12
  •  -10
  •   masfenix    16 年前

    像CPU核心一样多的线程是我经常听到的。