我正在研究一个多线程应用程序.
此应用程序以单个线程开始,并扩展为多个线程,以实现性能提升.
我有一个主线程,它将工作分成较小的块并将其卸载到处理块的工作线程.使用信号量控制该部分,以便在任何时候仅允许X个工作线程.工作线程产生数据块然后存储在队列或环形缓冲区中,然后由一个保存线程读取.该线程负责将数据块保存到磁盘(有时跨越本地网络).
我的开发机器是四核,内存为8GB.在我的机器上运行具有3个工作线程和1个保护程序线程的应用程序,可以在网络上稳定地传输数据,处理器平均使用率为75%.
第二种攻击这个问题的方法是在工作线程和保护程序线程之间添加另一组线程(即从当前工作线程中取出一个任务并将其添加到另一个线程)(我还为每个线程添加一个队列)这些线程)应用程序似乎没有在我的机器上获得任何速度,因为似乎有太多争用资源RAM总线饱和和处理器争用.
通过对线程数量及其优先级的大量实验,我找到了我的机器的理想设置,用于解决此问题的第一种和第二种方法.现在生产机器将拥有8个内核和64GB内存.必须为它配置一个非常不同的环境和应用程序.
我的问题是,你在什么时候创造了太多线程?是否总是需要通过实验来确定给定机器的理想设置?是否有一种方法可以确定或观察锁定是否过多地远离应用程序?
(我没有使用线程池,因为它不适合我的需求,长时间运行的线程由信号量和其他锁定机制管理.)
当应用程序的整体性能下降或者对同一个盒子上运行的其他应用程序的影响受到不可接受的负面影响时,您创建了太多线程.
关键是没有绝对的答案.
我一直在研究的一个应用程序使用1000个线程的线程池,对于我们正在做的事情,这似乎是正确的数字.在一种配置中,我们没有限制它,它达到了30,000+并且基本上使机器停止运转.
您基本上必须对其进行性能测试并拥有足够的监视/检测来确定应用程序的总体吞吐量,资源使用情况,线程利用率以及知道空闲线程的工作时间以及等待队列获取的工作时间.然后根据需要进行调整.
一个注意事项:在添加另一层线程之前要仔细考虑.我相信你知道,编写多线程代码很难.尽量保持简单.添加另一层是一个冒险的步骤.
没有人可以给你一个简单的数字答案,因为它过于依赖,不仅仅依赖于机器有多少核心,还有机器应该与你的应用程序同时执行的其他任务(如果有的话),以及关于你的线程究竟在做什么.
举一个后一个问题的例子:我曾经有一个非常简单的"爬虫",其中一定数量的线程专门用于我确定需要的HTTP GET页面 - 每个线程大部分时间都被阻塞在套接字调用中做HTTP GET,所以为了获得相当好的性能,我需要大量的(数百个).后来我改变了底层的方法来使用异步网络I/O而不是阻塞套接字 - 突然之间每个线程很容易就有数百个"飞行中"的URL,所以让数百个这样的线程处于活动状态会使系统不堪重负,可能导致更多的套接字打开比系统可以处理(它不是一个非常大或慷慨配置的服务器! - )导致崩溃,或至少由于过度交换等可怕的减速.
因此,即使对于完全受I/O约束的线程,它们正在使用的I/O的确切形式(例如阻塞或异步)将对线程数(或进程或任何其他此类单元)的数量产生巨大影响.最适合某个整体软件任务.执行更多CPU限制工作的线程显然必须针对内核的可用性以及内核可以工作的RAM进行校准(以获得最大性能),但也可能是其他资源(例如,如果某些线程能够使用可用的话) GPU或其他专用处理单元委托他们的一些工作).
最后,一旦你知道了所有这些参数,你就可以做出合理的估算,但是你可能会因为一个重要因素而离开 - 所以,对现实的工作量进行基准测试,比如说(一半)和两倍的线程因为您的猜测应该是最佳的,这是在部署时性能调优的后期阶段花费一些时间和资源的绝佳方式.总的来说,OFTEN的性能行为即使对于经验丰富的架构师,开发人员和系统管理员来说也是令人惊讶的,因此实际基准测试,仔细测量和相应调整的经验数据驱动方法并没有真正的替代品.(请注意,盲目的经验主义 - 只是试图适应实验观察而没有任何合理的模型来理解它们 - 几乎与教条和理论方法一样糟糕,忽略了数据,但是,这是另一种咆哮;-).