多线程与多进程的区别

51

我是新手,需要您的建议。

我需要构建一个应用程序,但是我无法让它快速计算。我已经尝试过Intel TBB,它很容易使用,但我从未使用过其他库。

在多处理器编程中,我正在研究OpenMP和Boost用于多线程,但我不知道它们的优缺点。

在C++中,什么时候使用多线程编程比多处理器编程优势更大,反之亦然?在处理繁重计算或启动大量任务时,哪种更适合?当我们使用它们构建应用程序时,它们的优缺点是什么?最后,哪个库最适合工作?


1
在使用像boost这样的线程库的多处理器机器上,可以利用可用的核心。您还可以在一个处理器上拥有多个线程,这些线程可以与Pentium 4中的超线程等技术交错执行。您是指多进程和分布式系统还是多线程?实际上您两者都可以做到,但线程的本质是获得并行性而不考虑处理器。FYI,据我所知,boost基于posix线程,我发现它非常容易使用。此外,新的C++0x标准将包括本地支持线程的功能。 - AJG85
你认为Boost和Intel TBB怎么样?它比Intel TBB更快吗?如果我有英特尔处理器,我应该选择英特尔吗? - bdelmas
Intel TBB在某些领域具有优势,例如提供了优化的并行原语,如并行for循环等,有助于在Intel处理器以及其他并行计算中创建并行散射-聚集样式算法。Boost线程主要是一个跨平台线程包,可在许多类型的硬件和操作系统平台上运行。但如果您需要某些特定的并行计算原语,则Boost线程不会直接提供,您必须自己编写代码。因此,您可以将TBB视为比Boost更高层次的抽象。 - Jason
1
从未使用过Intel TBB,所以无法说,但您的性能大多取决于您的实现。我选择boost是因为它具有跨平台性,因此异步任务服务器可以分布在Linux或Windows机器上。我更喜欢boost::thread而不是Windows线程或QThread,后者是我使用过的唯一其他线程。TBB维基文章让它听起来比boost更高级,因此根据您正在寻找的线程控制级别,最初实施可能会更容易:http://en.wikipedia.org/wiki/Intel_Threading_Building_Blocks - AJG85
1
一个重要的区别是:当线程崩溃时,进程会停止。当线程挂起时,进程可能会停止,或者你需要杀死它并担心内存状态。这意味着多进程可以更加健壮。例如,对于服务器来说,至少应该有一个监控/启动进程与实际执行进程分开。 - Matthieu M.
对于你的问题,一个合适的答案取决于需要解决的问题的性质和你所针对的平台。有一整类问题无法通过并行编程加速解决。如果你能详细说明你的问题,一定会帮助你获得更实用、具体的答案。 - Sean McSomething
4个回答

75

多线程意味着运行多个线程。这可以在单处理器系统上或多处理器系统上完成。

在单处理器系统上,运行多个线程时,计算机同时执行多个任务(即多任务处理)的实际观察是一种幻觉,因为在幕后的真正情况是,有一个软件调度程序在单个 CPU 上进行时间分片。因此,在任何给定时间,只有一个任务正在运行,但调度程序在任务之间快速切换,以便您从未注意到有多个进程、线程等争用同一 CPU 资源。

在多处理器系统上,需要减少时间分片的需求。时间分片效应仍然存在,因为现代操作系统可能有数百个线程争用两个或更多处理器,并且通常不存在线程数量与可用处理核心数量的1:1关系。因此,在某个时候,一个线程将不得不停止,另一个线程开始在两个线程共享的 CPU 上运行。这也由操作系统的调度程序处理。尽管如此,使用多处理器系统,您可以同时发生两件事情,这与单处理器系统不同。

从本质上讲,这两种范式是有些正交的,因为只要想要有两个或多个任务异步运行,就需要使用多线程,但由于时间片分配,不一定需要一个多处理器系统来完成。如果你试图运行多个线程,并且正在进行高度并行的任务(例如,尝试解决积分),那么是的,你可以将更多的核心投入到问题中。你不一定需要线程与处理器核心之间的1对1关系,但同时,你也不希望启动太多线程,以至于它们必须等待在可用CPU核心上调度而产生大量空闲线程。另一方面,如果你的并行任务需要一些连续组件,即一个线程将在等待另一个线程的结果之后才能继续,那么你可能可以运行更多线程,采用某种障碍或同步方法使需要空闲的线程不会消耗CPU时间,只有需要运行的线程竞争CPU资源。

7
你打字速度真快!我只想添加我的一点意见作为评论。 - AJG85
所有的评论对我来说都很好 :) @ Jason,所以线程是设计用来编写应用程序的许多重要部分并完成它们的工作,对吧? - bdelmas
1
一个线程可以用于执行任何任务,这个任务可以是任何东西。它可以是整个程序运行期间持续执行的任务,例如网络守护进程会在套接字上“监听”连接,然后产生更多的线程来管理这些连接。线程也可以用于执行小任务,比如解决高度并发的积分迭代问题。 - Jason
你在程序中到处都使用多线程吗?我应该什么时候停止将我的程序分解成线程?使用多进程库来进行大型计算(如Intel TBB,OpenMP)不是更好吗? - bdelmas
那些类型的库可以帮助计算任务,因为它们是对原始线程库(如Boost、pthreads等)的更高级抽象,但每当您需要同时执行多个任务时(我用引号说这句话,因为在单处理器系统上它是一种幻觉),您将需要为每个任务使用单独的线程。然而,有很多情况下,您正在编写只执行一件事情的实用程序,因此您不需要为这些类型的应用程序使用线程。例如,像grepsedcp等命令行应用程序就不是多线程的。 - Jason

27

我认为应该在@Jason的优秀答案中添加一些重要观点。

首先,即使在单处理器上,多线程并不总是虚幻的 - 有些操作不涉及处理器。主要是I / O - 磁盘,网络,终端等。这种操作的基本形式是阻塞同步,即您的程序等待操作完成然后继续。等待时,CPU会切换到另一个进程/线程。

如果您在此期间可以做任何事情(例如在等待用户输入时进行后台计算,提供另一个请求等),则基本上有两个选择:

  • 使用异步I / O:调用非阻塞I / O,并向其提供回调函数,告诉它“在完成时调用此函数”。调用立即返回,I/O操作在后台继续。继续进行其他工作。

  • 使用多线程:每种任务都有一个专用线程。当一个线程等待阻塞I / O调用时,另一个线程继续运行。

两种方法都是困难的编程范例,各自都有利弊。

  • 使用异步I / O时,程序逻辑的逻辑不太明显,很难跟踪和调试。但是您可以避免线程安全问题。
  • 对于线程,挑战在于编写线程安全程序。线程安全故障是非常难以重现的恶性BUG。过多使用锁可能会导致性能下降而不是提高性能。

(转向多处理)

在Windows上流行的多线程是因为操作进程在Windows上相当繁重(创建进程,上下文切换等),而线程要轻量得多(至少在我工作的Win2K上是这种情况)。

在Linux / Unix上,进程要轻量得多。此外(据我所知),Linux上的线程实际上是一种内部进程,因此在线程与进程之间进行上下文切换方面没有任何优势。然而,您需要使用某种形式的IPC(进程间通信),例如共享内存、管道、消息队列等。

更有趣的是,请看SQLite FAQ,它宣称“线程是邪恶的”! :)


2
还有第三种选择,通过select()/poll()/等方式复用I/O。这可能比多线程更安全,比异步I/O更易于理解。 - Jeremy Friesner

5
回答第一个问题: 最好的方法是在您的代码中使用多线程技术,直到即使这样仍无法为您带来足够的好处。假设操作系统会处理对多个处理器的委派,如果它们可用。
如果您实际上正在解决多线程不足以解决问题的问题(即使具有多个处理器),或者如果您正在运行不使用其多个处理器的操作系统,则可以考虑如何获取更多的动力。这可能意味着在网络上将进程分配到其他计算机上。
我没有使用TBB,但我使用过IPP并发现它效率高并且设计良好。Boost是可移植的。

0
只想提一下,基于流程的编程( http://www.jpaulmorrison.com/fbp )范式是一种自然的多程序/多处理方式的应用程序开发方法。它提供了从高层到低层的一致应用视图。Java和C#的实现利用了机器上的所有处理器,但旧的C++实现只使用一个处理器。然而,通过在连接上使用锁定,它可以相当轻松地扩展到使用BOOST(或pthread,我猜)。我曾经开始转换为使用光纤,但我不确定继续这个路线是否有意义。 :-) 希望能得到反馈。顺便说一句,Java和C#的实现甚至可以使用套接字进行互通。

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