我正在编写服务器,并且在收到请求时将每个动作发送到一个单独的线程中。我这样做是因为几乎每个请求都会进行数据库查询。我正在使用线程池库来减少线程的构造/销毁。
我的问题是:这样的I / O线程的最佳切入点是什么?我知道这只是一个粗略的估计,但是我们正在讨论数百个吗?几千
我将如何确定这个临界值?
编辑:
谢谢大家的答复,看来我只是必须对其进行测试才能确定我的线程数上限。但问题是:我怎么知道我已经达到那个上限了?我到底应该测量什么?
我正在编写服务器,并且在收到请求时将每个动作发送到一个单独的线程中。我这样做是因为几乎每个请求都会进行数据库查询。我正在使用线程池库来减少线程的构造/销毁。
我的问题是:这样的I / O线程的最佳切入点是什么?我知道这只是一个粗略的估计,但是我们正在讨论数百个吗?几千
我将如何确定这个临界值?
谢谢大家的答复,看来我只是必须对其进行测试才能确定我的线程数上限。但问题是:我怎么知道我已经达到那个上限了?我到底应该测量什么?
Answers:
有人会说两个线程太多了-我不在那个阵营里:-)
这是我的建议:衡量,不要猜测。一种建议是使其可配置,并将其初始设置为100,然后将您的软件发布并监视发生的情况。
如果您的线程使用量达到3的峰值,那么100太多了。如果在一天的大部分时间里保持在100,将其提高到200,然后看看会发生什么。
实际上,您可以让代码本身监视使用情况,并在下次启动时调整配置,但这可能太过分了。
为了澄清和阐述:
我不主张滚动自己的线程池子系统,请务必使用现有的线程池子系统。但是,由于您询问的是线程的一个好起点,因此我假设您的线程池实现能够限制创建的最大线程数(这是一件好事)。
我已经编写了线程和数据库连接池代码,它们具有以下功能(我认为这对于性能至关重要):
第一个为线程池客户端设置了最低性能的基准(此数量的线程始终可用)。第二个方法设置了活动线程对资源使用的限制。第三个使您在安静的时候回到基线,以最大程度地减少资源使用。
您需要在没有使用线程(A)的资源使用与没有足够的线程来完成工作(B)的资源使用之间取得平衡。
(A)通常是内存使用情况(堆栈等),因为不执行任何操作的线程不会占用大量CPU。(B)通常会延迟请求的处理时间,因为您需要等待线程可用。
这就是为什么要测量。如您所说,您的绝大多数线程将等待数据库的响应,因此它们将不会运行。有两个因素影响应允许的线程数。
第一个是可用的数据库连接数。除非您可以在DBMS上增加它,否则这可能是一个硬性限制-在这种情况下,我将假设您的DBMS可以进行无限数量的连接(尽管理想情况下您也应该进行测量)。
然后,您应该拥有的线程数取决于您的历史使用情况。您应该运行的最小值是您曾经运行的最小值+ A%,绝对最小值为(例如,使其像A一样可配置)5。
最大线程数应为您的历史最大值+ B%。
您还应该监视行为更改。如果由于某种原因,您的使用率在相当长的一段时间内达到可用状态的100%(这样会影响客户端的性能),则应提高允许的最大值,直到再次提高B%。
回应“我应该精确测量什么?” 题:
您应该具体衡量的是负载下并发使用(例如,等待DB调用返回)的最大线程数。然后添加例如 10%的安全系数(强调,因为其他张贴者似乎以我的示例为固定建议)。
另外,这应该在生产环境中进行调整。可以事先获得估算值,但是您永远不知道哪种生产方式会影响您的生产(这就是为什么所有这些东西都应该在运行时可配置的原因)。这是为了应对意外情况,例如传入的客户端呼叫意外加倍。
这个问题已经进行了非常彻底的讨论,我没有机会阅读所有答案。但是,在考虑可以在给定系统中和平共存的并发线程数的上限时,需要考虑以下几件事。
现在,您可以调整堆栈大小以合并更多线程,但随后必须考虑线程管理(创建/销毁和调度)的开销。您可以将CPU Affinity强制应用于给定的进程以及给定的线程,以将它们绑定到特定的CPU,以避免CPU之间的线程迁移开销并避免冷钱问题。
请注意,一个人可以随意创建数千个线程,但是当Linux用完VM时,它只是随机地开始杀死进程(因此线程)。这是为了防止实用程序配置文件被最大化。(效用函数说明给定资源量的系统范围的效用。在这种情况下,如果使用恒定的资源CPU周期和内存,则效用曲线会随着越来越多的任务而趋于平坦)。
我确定Windows内核调度程序也可以进行此类操作以应对资源的过度利用
如果您的线程正在执行任何类型的资源密集型工作(CPU /磁盘),那么您几乎看不到一两个以上的好处,太多的好处将很快导致性能下降。
“最佳情况”是,您的后面的线程将在第一个线程完成时停止运行,或者某些线程在资源上的开销较小且争用程度较低。最坏的情况是您开始破坏缓存/磁盘/网络,并且总体吞吐量下降。
一个好的解决方案是将请求放置在一个池中,然后将其从线程池分派给工作线程(是的,避免连续创建/销毁线程是一个重要的第一步)。
然后,可以根据分析的结果,运行的硬件以及计算机上可能发生的其他情况,来调整和扩展该池中活动线程的数量并进行缩放。
您应该记住的一件事是,python(至少是基于C的版本)使用了所谓的全局解释器锁,该锁可能会对多核计算机的性能产生巨大影响。
如果您真的最需要多线程python,则可以考虑使用Jython或其他工具。
就像Pax正确说的那样,测量,不要猜测。我为DNSwitness所做的工作和结果令人惊讶:理想的线程数比我想象的要高得多,大约15,000个线程才能获得最快的结果。
当然,这取决于很多事情,这就是为什么您必须衡量自己。
执行全面措施(仅法语)?。
“大铁”的答案通常是每个有限资源(处理器(与CPU绑定),臂(与I / O绑定)等)只有一个线程,但是只有在您可以将工作路由到资源的正确线程时才能起作用。被访问。
在不可能的情况下,请考虑您拥有可替代的资源(CPU)和不可替代的资源(武器)。对于CPU而言,将每个线程分配给特定的CPU并不是至关重要的(尽管它有助于缓存管理),但是对于Arm而言,如果您不能为Arm分配线程,则会进入排队理论以及保持Arm的最佳数量忙。通常,我在想,如果您不能基于使用的分支路由请求,那么每个分支有2-3个线程将是正确的。
当传递给线程的工作单元无法执行合理的原子工作单元时,就会出现复杂问题。例如,您可能让线程在某一点访问磁盘,而在另一点等待网络。这增加了其他线程可以进入并完成有用工作的“裂缝”的数量,但同时也增加了其他线程污染彼此的缓存等并使系统瘫痪的机会。
当然,您必须权衡所有这些对线程的“重量”。不幸的是,大多数系统都有非常重量级的线程(它们所谓的“轻量级线程”通常根本不是线程),因此最好在低端进行。
我在实践中看到的是,非常细微的差异可以使最佳线程数产生巨大差异。特别是,缓存问题和锁冲突会大大限制实际的并发量。
要考虑的一件事是机器上将要执行代码的内核数。这表示在任何给定时间可以处理多少个线程的硬限制。但是,如果像您的情况那样,预计线程会频繁地等待数据库执行查询,则您可能希望根据数据库可以处理的并发查询数来调整线程。
我认为这对您的问题有些疑惑,但是为什么不将它们分叉到流程中呢?我对网络的理解(从过去的朦胧时代开始,我根本就不编码网络)是,每个传入的连接都可以作为一个单独的过程来处理,因为如果有人在您的过程中做了一些令人讨厌的事情,核对整个程序。
在大多数情况下,应允许线程池处理此问题。如果您发布一些代码或提供更多详细信息,则可能会更容易查看是否有某种原因导致线程池的默认行为不是最佳选择。
您可以在此处找到有关其工作方式的更多信息:http : //en.wikipedia.org/wiki/Thread_pool_pattern
我经常听到与CPU内核一样多的线程。