创建多少个线程以及何时创建?

10

我有一个与网络相关的Linux应用程序,它从多个源接收RTP流,进行非常简单的数据包修改,然后将流转发到最终目的地。

我应该如何决定需要多少线程来处理这些数据?我认为不能为每个RTP流打开一个线程,因为可能会有成千上万个。我应该考虑CPU核心的数量吗?还有什么其他重要因素吗? 谢谢。


实际上,在Linux 2.6下,可能会有成千上万的线程工作,但这可能不是最好的方法 - 请参阅我的回复。 - MarkR
7个回答

22

了解在服务器上使用多个线程的目的非常重要;在服务器中,许多线程的作用是减少延迟而不是增加速度。你不能通过拥有更多线程来使CPU更快,但你可以使一个线程在给定时间内始终出现以处理请求。

拥有一堆只是并行移动数据的线程是一种相当低效的方法(自然地,为每个请求创建一个线程会完全失败)。使用线程池模式可以是一个更有效、专注于减少延迟的方法。

现在,在线程池中,你至少要有和你的CPU/核心数量一样多的线程。你可以拥有比这更多的线程,但额外的线程只会再次减少延迟而不是增加速度。

把组织服务器线程的问题看作是组织超市排队的问题。你想要很多工作较慢的收银员还是一个工作超级快的收银员?快速收银员的问题不是速度,而是一个购物车很多的顾客可能仍然需要花费他们很多的时间。许多线程的需求来自于可能有一些请求需要很长时间并阻塞所有线程的可能性。按照这种推理,无论你是否从许多较慢的收银员中受益,取决于你是否有相同数量或非常不同数量的杂货。回到基本模型,这意味着你必须玩弄你的线程数,以确定在给定流量特性下什么是最优的,观察处理每个请求所需时间。


10

传统上,合理线程的数量取决于执行单元的数量、IO与计算之比以及可用内存。

执行单元数量(XU

这个数值表示可以同时活动的线程数量。根据计算情况,这可能或可能不包括超线程——混合指令工作负载效果更好。

IO与计算之比(%IO

如果线程从不等待IO而是一直计算(%IO = 0),那么使用更多线程只会增加内存压力和上下文切换的开销。如果线程始终等待IO而从不计算(%IO = 1),那么使用poll()select()的变体可能是个好主意。

对于所有其他情况,XU / %IO 可以近似地确定需要多少线程才能完全利用可用的XU。

可用内存(Mem

这更像是一个上限。每个线程使用一定量的系统资源(MemUse)。Mem / MemUse 可以近似确定系统可以支持多少线程。

其他因素

即使您可以猜测或(更好的是)测量上述数字,整个系统的性能仍可能受到其他因素的限制。例如,可能还有另一个运行在该系统上的服务,它使用了一些XU和内存。另一个问题是可用的IO带宽(IOCap)。如果您每传输一个字节所需的计算资源比您的XUs提供的要少,那么显然您需要更少地关注完全利用它们,而更多地关注增加IO吞吐量。

有关后一个问题的更多信息,请参见这篇介绍屋顶线模型的Google Talk视频


4
我建议您尝试仅使用一个线程,这将使编程更加容易。虽然您需要使用类似libevent的东西来复用连接,但您不会遇到任何意外的同步问题。
一旦您拥有了一个可工作的单线程实现,您可以进行性能测试,并决定是否需要多线程实现。
即使需要多线程实现,如果它们没有很多共享数据,将其分成几个进程可能会更容易(即不共享地址空间;从父进程fork()或exec多个进程副本)。
您还可以考虑使用像Python的“Twisted”之类的东西来使实现更容易(这就是它的设计目的)。
实际上,也许没有使用线程而使用进程的好处 - 但也许在您的情况下有好处,很难说。这取决于您需要在线程之间共享多少数据。

2
我建议为此应用程序使用线程池。 http://threadpool.sourceforge.net/ 使用线程池来管理你的线程和队列。
稍后,你可以根据性能分析来调整使用的最大和最小线程数。

2

听从那些建议你使用libevent(或特定于操作系统的实用程序,如epoll / kqueue)的人。在许多连接的情况下,这是绝对必要的,因为像你说的那样,创建线程将会给性能带来巨大的影响,而select()也不完全满足要求。


1
让你的程序自行决定。添加代码来测量吞吐量,并动态增加/减少线程数量以最大化吞吐量。
这样,无论执行核心数量和其他因素如何,您的应用程序始终能够表现出色。

一个给定的线程池实现可能会自我优化(GNU Make 就是这样做的),但这绝不是现状。 - Tom

0

避免尝试为每个客户端请求创建一个(甚至N个)线程是一个好主意。这种方法经典上不可扩展,您肯定会遇到内存使用或上下文切换的问题。相反,您应该考虑使用线程池方法,并将传入的请求视为任何线程在池中处理的任务。然后,此方法的可伸缩性受理想线程数的限制 - 通常与CPU核心数有关。您希望尝试使每个线程在单个核心上恰好使用100%的CPU - 因此,在理想情况下,您将每个核心拥有1个线程,这将将上下文切换减少为零。根据任务的性质,这可能不可能,也许线程必须等待外部数据,或从磁盘读取或其他原因,因此您可能会发现线程数增加了一些比例。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接