我正在写一个服务器,当收到请求时,我将每个动作发送到一个单独的线程。我这样做是因为几乎每个请求都要进行数据库查询。我正在使用线程池库来减少线程的构造/破坏。
我的问题是:对于这样的I/O线程,什么是一个好的截断点?我知道这只是一个粗略的估计,但我们说的是几百吗?成千上万的吗?
我怎么才能算出这个界限呢?
编辑:
感谢大家的回复,似乎我只是要测试一下,以找出我的线程数上限。问题是:我怎么知道我已经达到上限了呢?我到底应该测量什么?
我正在写一个服务器,当收到请求时,我将每个动作发送到一个单独的线程。我这样做是因为几乎每个请求都要进行数据库查询。我正在使用线程池库来减少线程的构造/破坏。
我的问题是:对于这样的I/O线程,什么是一个好的截断点?我知道这只是一个粗略的估计,但我们说的是几百吗?成千上万的吗?
我怎么才能算出这个界限呢?
编辑:
感谢大家的回复,似乎我只是要测试一下,以找出我的线程数上限。问题是:我怎么知道我已经达到上限了呢?我到底应该测量什么?
当前回答
“大铁”的答案通常是每个有限的资源一个线程——处理器(CPU限制),臂(I/O限制)等等——但这只有在您能够将工作路由到要访问的资源的正确线程时才有效。
在不可能的情况下,考虑您拥有可替代资源(cpu)和不可替代资源(武器)。对于CPU来说,将每个线程分配给特定的CPU并不重要(尽管这有助于缓存管理),但对于手臂,如果不能将线程分配给手臂,则需要考虑排队理论以及让手臂忙碌的最佳数量。一般来说,我认为如果您不能基于所使用的臂路由请求,那么每个臂有2-3个线程将是正确的。
当传递给线程的工作单元没有执行合理的原子工作单元时,就会出现复杂情况。例如,你可以让线程在一个点访问磁盘,在另一个点在网络上等待。这增加了“裂缝”的数量,其他线程可以进入并做有用的工作,但它也增加了其他线程污染彼此缓存的机会,等等,并使系统陷入困境。
当然,您必须将所有这些与线程的“重量”进行权衡。不幸的是,大多数系统都有非常重量级的线程(而他们所谓的“轻量级线程”通常根本就不是线程),所以最好在低级别上犯错。
我在实践中看到的是,非常细微的差异可能会对最佳线程的数量产生巨大的影响。特别是,缓存问题和锁冲突会极大地限制实际并发的数量。
其他回答
正如帕克斯所说,衡量,而不是猜测。这就是我为DNSwitness所做的事情,结果令人惊讶:理想的线程数比我想象的要高得多,大约15,000个线程才能获得最快的结果。
当然,这取决于很多东西,这就是为什么你必须衡量自己。
在Combien de fils d'exécution ?中的完整测量(仅法语)。
需要考虑的一件事是,将执行代码的机器上有多少内核。这表示在任何给定时间内可以处理多少线程的硬限制。但是,如果像您的情况一样,线程需要频繁地等待数据库执行查询,那么您可能需要根据数据库可以处理多少并发查询来调优线程。
有些人会说两个线程太多了-我不属于那个阵营:-)
我的建议是:衡量,不要猜测。一个建议是将其设置为可配置,并最初将其设置为100,然后将软件发布到野外并监视发生的情况。
如果线程使用的峰值是3,那么100就太多了。如果一天的大部分时间都保持在100,那就把它提高到200,看看会发生什么。
实际上,您可以让您的代码本身监视使用情况,并在下次启动时调整配置,但这可能是多余的。
为了澄清和阐述:
我不提倡滚动您自己的线程池子系统,务必使用您现有的线程池子系统。但是,由于您正在询问线程的良好分界点,我假设您的线程池实现能够限制创建的线程的最大数量(这是一件好事)。
我已经编写了线程和数据库连接池代码,它们具有以下特性(我认为这对性能至关重要):
活动线程的最小数量。 线程的最大数目。 关闭一段时间没有使用的线程。
第一个设置线程池客户机的最低性能基线(这个线程数量总是可用的)。第二个设置活动线程使用资源的限制。第三种是在安静的时候让你回到基线,以尽量减少资源的使用。
您需要平衡拥有未使用线程的资源使用(A)和没有足够线程执行工作的资源使用(B)。
(A)通常是内存使用情况(堆栈等),因为不做任何工作的线程不会使用太多CPU。(B)通常会延迟处理请求,因为他们到达,因为你需要等待一个线程变得可用。
这就是为什么要测量。在此过程中,绝大多数线程将等待数据库的响应,因此它们不会运行。有两个因素影响您应该允许多少线程。
第一个是可用的DB连接数量。这可能是一个硬限制,除非您可以在DBMS中增加它——我将假设您的DBMS在这种情况下可以接受无限数量的连接(尽管您也应该理想地测量它)。
那么,线程的数量应该取决于历史使用情况。您应该运行的最小值是您曾经运行过的最小值+ A%,具有绝对最小值(例如,并使其像A一样可配置)5。
最大线程数应该是历史最大值+ B%。
您还应该监视行为变化。如果由于某种原因,您的使用率在很长一段时间内达到了可用容量的100%(因此会影响客户机的性能),那么您应该提高允许的最大值,直到它再次高B%。
在回答“我究竟应该测量什么?”的问题时:
您应该具体测量的是负载下并发使用的线程的最大数量(例如,等待DB调用的返回)。然后添加一个10%的安全系数(强调一下,因为其他海报似乎把我的例子作为固定的建议)。
此外,这应该在生产环境中进行调优。事先进行估计是可以的,但您永远不知道生产过程中会出现什么情况(这就是为什么所有这些东西都应该在运行时可配置)。这是为了捕捉一种情况,例如意想不到的双倍客户端调用。
如果您的线程正在执行任何类型的资源密集型工作(CPU/磁盘),那么您很少会看到超过一到两个的好处,太多会很快降低性能。
“最好的情况”是,当第一个线程完成时,后面的线程将会停止,或者一些线程在资源上有低开销的块,且资源争用低。最坏的情况是您开始折腾缓存/磁盘/网络,并且您的总体吞吐量下降到地板。
一个好的解决方案是将请求放在一个池中,然后从线程池分派给工作线程(是的,避免持续的线程创建/销毁是很好的第一步)。
然后,可以根据概要分析的结果、正在运行的硬件以及机器上可能发生的其他情况调整和缩放此池中的活动线程数。
我认为这有点回避你的问题,但是为什么不把它们分成进程呢?我对网络的理解(从以前模糊的日子里,我根本就没有编写网络代码)是每个传入的连接都可以作为一个单独的进程来处理,因为如果有人在您的进程中做了一些讨厌的事情,它不会破坏整个程序。