什么构成了在编程中正确使用线程?


13

我厌烦听到人们建议您每个处理器仅使用一个线程,而许多程序每个进程最多使用100个线程!以一些常见程序为例

vb.net ide uses about 25 thread when not debugging
System uses about 100
chrome uses about 19
Avira uses more than about 50

每当我发布与线程相关的问题时,几乎每次都会提醒我,每个处理器不应使用一个以上的线程,并且上面提到的所有程序在使用单个处理器的系统上都会崩溃。


7
该建议是广泛的。每个处理器一个线程的限制仅适用于受计算限制的应用程序。大多数程序都是IO绑定的,无论是网络流量,磁盘访问甚至RAM。这就是为什么Web服务器,数据库等具有线程池的线程池比处理器核心多得多的原因。
Kilian Foth 2011年

2
“几乎每次都提醒我,每个处理器不应使用多个线程”?您可以发布链接或示例吗? 几乎每次
S.Lott

2
“ ...人们建议您每个进程只使用一个线程。” 这些人是谁?自黑暗时代以来,调度已显着发展。
Rein Henrichs

2
每个进程最多只能有一个UI线程
Slaks 2011年

3
@Billy ONeal,您的编辑使问题变得毫无意义
SK-logic

Answers:


22

每个处理器只能使用一个线程,

在HPC中,您可能需要最大的效率-否则,我今天听到的最愚蠢的事情!

您应该使用适合程序设计的线程数,并且仍然可以接受性能。

对于Web服务器,为每个传入的连接激发一个线程可能是合理的(尽管对于负载很重的服务器有更好的方法)。

对于一个想法,每个在其自己的线程中运行的工具并非没有道理。我怀疑为.Net IDE报告的许多线程都是在自己的线程中启动的日志记录和I / O任务之类的东西,因此它们可以继续畅通无阻。


9
现在,您让我想知道您听说过的最愚蠢的事情是什么!
Michael K

3
@Michael-我教过本科生并从事过国防合同工作-您不会相信我所听到的最愚蠢的事情!
Martin Beckett

1
我们在TheDailyWTF.com上看到了吗?
FrustratedWithFormsDesigner

我现在真的找不到它们,但请看此链接social.msdn.microsoft.com/Forums/en-US/vbgeneral/thread/…–
Smith Smith

2
拥有最多每分配给应用处理器一个CPU绑定线程。与IO绑定的线程不是一个大问题(除了它们消耗的内存之外),重要的是要记住,可以将应用程序限制为仅使用系统CPU的一部分。毕竟,它(通常)是用户/管理员的计算机,而不是程序员的计算机。
Donal Fellows,

2

如果目的是通过并行执行来提高速度,则可以使用“单线程单线程”建议。

一个完全不同且同等有效的原因是代码必须响应不可预测的事件时的简单性。因此,如果一个程序必须侦听100个套接字,并且似乎要全神贯注于每个套接字,那么这是线程处理的理想选择。另一个示例是UI,其中一个线程处理UI事件,而另一个则执行后台处理。


1
IO绑定处理可以作为每个事件源一个线程来完成,或者可以将多个事件源多路复用到单个线程上。复用代码通常更复杂,更高效。
Donal Fellows,

2

您希望每个计算都有一个线程,该线程可以以与其他计算不同的速率进行。

对于需要大量工作的并行CPU绑定计算,通常每个CPU需要一个线程,因为一旦它们繁忙,更多的线程将无济于事,只会增加调度程序的开销。如果工作块的时间大小不规则,或者在运行时动态生成(通常在要处理的复杂数据结构较大时发生),则可能需要将这些块附加到大量线程上,因此调度程序始终具有较大的线程数。设置为从某些工作完成时选择,以保持所有CPU繁忙。

对于I / O绑定计算,通常需要为每个独立的I / O“通道”分配一个线程,因为它们以不同的速率进行通信,并且阻塞在通道上的线程不会阻止其他线程取得进展。


请注意,这种线程处理方式可能会导致某些奇怪的程序。我看过一个4线程程序,该程序有一个线程可以从数据库表中读取记录,一个线程可以将转换后的记录写入套接字,一个线程可以读取这些套接字写操作的答案(这些命令无序返回)和异步),以及使用答案修改原始数据库记录的线程。随后出现了不直观的错误情况。
Bruce Ediger

一种观点认为这种风格会产生奇怪的程序。另一种观点是程序应该具有的自然风格。Dunno关于“不直观”的错误情况;如果发生了很多事情,其中​​之一发生了错误,那么对于许多语言来说,确保将其正确地传播到异步计算中是一个问题[愚蠢的是,未在线程边界处定义Java异常],但不是程序样式有问题。(我们的PARLANSE编程语言[请参见我的简介]可以清晰地处理线程边界上的异常,因此可以正确执行此操作。)
艾拉·巴克斯特

1

线程的经验法则是,您希望计算机上可用的每个“执行单元”至少有一个“活动”(能够在给定CPU时间后立即执行其命令)的工作线程。“执行单元”是一个逻辑指令处理器,因此四芯片,四核Xeon超线程服务器将具有32个EU(4个芯片,每个芯片4个内核,每个超线程)。您的平均Core i7值为8。

如果每个EU始终处于运行状态,则每个EU最多可以使用一个线程。几乎从来没有这种情况,因为线程需要访问必须等待的非缓存内存,硬盘,网络端口等,并且不需要活跃的CPU来执行。因此,您可以通过排队等待并等待更多线程来进一步提高整体效率。这确实是有代价的。当一个CPU切换一个线程时,它必须缓存该线程的寄存器,执行指针和其他状态信息,这些信息通常保存在EU的最内部工作中,并且必须非常快速地访问,以允许该CPU芯片中的其他EU来取走它。它还需要操作系统中的线程来决定应切换到哪个线程。最后,当欧盟更换线程时,它失去了大多数处理器体系结构使用的流水线性能提升;它必须在切换线程之前冲洗管道。但是,由于平均而言,与仅等待硬盘驱动器甚至RAM返回信息相比,所有这些平均花费的时间要少得多,所以值得付出成本。

但是,总的来说,一旦您获得的“活动”线程数量是欧盟的两倍,操作系统就会开始花费更多的欧盟时间安排线程,并且欧盟之间进行切换的时间会比实际花费在运行活动线程上的时间更多程序。这是规模不经济的重点;如果此时要添加一个额外的线程,则运行多线程算法实际上将花费更长的时间。

因此,总的来说,您要在程序中维护的线程数至少要与计算机上具有EU的线程数相同,但您要避免使没有等待或休眠的线程数增加一倍以上。


如果N是线程数,U是单元数,则OP质疑“ N = U”规则。您将其放宽为“ U <= N <= 2 U”规则。我再说一点,说“ N <= c U”对于“合理地小的”常量(程序员已知)c是可以接受的(如果基准测试显示合理的性能)。我非常担心线程数是否可以增加到潜在的无限数量。
5gon12eder

1

您应该将一个线程用于:

您需要保持忙​​碌的每个处理器。

您可以同时挂起每个不能以非阻塞方式执行的I / O。(例如,从本地磁盘读取。)

每个需要专用线程的任务,例如,调用不具有非阻塞接口或不适合使用非阻塞接口的库。这包括监视系统时钟,触发计时器等任务。

一些额外的功能可以防止意外阻止,例如页面错误。

还有一些额外的措施可防止发生不值得优化的预期阻塞,例如在非关键代码中。(例如,如果您可能很少需要执行DNS请求,则异步进行DNS请求可能不值得。只需创建一些额外的线程并简化您的工作即可。)

如果遵循“每个处理器一个线程”的规则,则所有代码对性能都至关重要。由于某种原因而阻塞的任何代码都意味着您的进程无法使用该处理器。没有充分的理由,这会使编程变得更加困难。


0

您可以生成进程和线程来为单个程序启用多核\多处理器系统的使用,在这种情况下,拥有更多的线程/进程而不是核心不会(至少对于单个程序而言)无益。

或者,您可以具有轮询事件的例程,该例程通常会阻止进一步执行。与其将CPU与轮询绑定在一起,不如创建一个线程,该线程将处于空闲状态,直到适当的事件将其唤醒为止。此方法在Web服务器和GUI事件队列中非常常用。大多数程序都希望拥有某种中央数据存储区(即使是其程序执行代码),所有线程都可以访问该存储区,所以我想这就是为什么它们在进程上使用线程。


0

您提到的应用程序很少同时运行所有这数十个线程。他们中的大多数人只是坐在那里,因为它们在线程池中。该应用程序将各种任务发送到队列,该队列由线程池中的线程清除。

那为什么游泳池那么大呢?因为,线程通常必须等待其他资源,例如磁盘,网络,用户,某些其他线程等。当线程正在等待时,运行其他线程以充分利用处理器是适当的。但是,适当地调整池的大小是很棘手的。线程太少,您将失去性能,因为在等待某些东西时处理器没有得到充分利用。线程太多,由于它们之间的切换,您将失去性能。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.