2011-06-17 197 views
24

我是这种编程的新手,需要您的观点。多线程与多处理

我必须建立一个应用程序,但我不能让它计算得足够快。我已经尝试过英特尔TBB,它很容易使用,但我从未使用过其他库。

在多处理器编程中,我正在阅读有关多线程的OpenMP和Boost,但我不知道它们的优缺点。

在C++中,多线程编程与多处理器编程相比有何优势,反之亦然?哪种方法最适合繁重的计算或启动许多任务...?当我们构建与他们一起设计的应用程序时,他们有什么优点和缺点?最后,哪个库最适合与?

+1

在使用线程库(如boost)的多处理器机器中,将利用可用内核。您也可以在一个处理器上有多个线程,这些线程可以与Pentium 4中的超线程技术交织。您是指多进程,分布式系统还是多线程?实际上你可以做到这两点,但线程的本质是无论处理器如何都可以获得并行性。 只要我知道,FYI提升基于posix线程,我发现它很容易使用。此外,新的C++ 0x标准将包括语言原生的线程支持。 – AJG85

+0

您对Boost和Intel TBB有什么看法?它比Intel TBB更快?或者如果我有英特尔处理器,我应该去英特尔? – Nazka

+0

英特尔TBB在某些提供优化的并行基元(如并行for循环等)的领域具有优势,有助于在Intel处理器上创建并行分散 - 聚集样式算法以及其他并行计算。 Boost线程主要是一个跨平台的线程包,可以在多种类型的硬件和操作系统平台上运行。如果你需要某些并行计算原语,Boost线程并不直接提供这些,你必须自己编写它们。所以你可以把TBB视为比Boost更高的抽象层次。 – Jason

回答

50

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

在单处理器系统上,当运行多个线程时,计算机同时执行多个任务(即多任务)的实际观察结果是一种幻觉,因为真正发生的情况是:是在单个CPU上执行时间分片的软件调度程序。因此,在任何给定的时间只有一个任务正在发生,但是调度程序在任务之间切换的速度足够快,这样您就不会注意到有多个进程,线程等竞争相同的CPU资源。

在多处理器系统上,减少了对时间片的需求。时间切片效应仍然存在,因为现代操作系统可能会有数百个线程争夺两个或更多处理器,并且线程数量与可用处理内核数量之间通常不存在1对1关系。所以在某些时候,一个线程将不得不停止,另一个线程在两个线程共享的CPU上启动。这又由操作系统的调度程序处理。这就是说,使用多处理器系统,您可以在之间同时发生两件事情,这与单处理器系统不同。最后,这两种范例实际上有点正交,因为当您想要两个或多个任务异步运行时,您将需要多线程,但由于时间片化,您不一定需要多线程,处理器系统来实现这一点。如果你正在尝试运行多个线程,并且正在做一个高度并行的任务(即试图解决一个整体问题),那么是的,你可以抛出的核心越多越好。您不一定需要线程和处理内核之间的1对1关系,但同时,您不希望分离出太多的线程,最终导致大量空闲线程,因为它们必须等待安排在其中一个可用的CPU内核上。另一方面,如果你的并行任务需要一些顺序组件,也就是说,一个线程将在另一个线程继续运行之前等待其结果,那么你可以使用某种类型的屏障或同步方法运行更多的线程,需要空闲的线程不会使用CPU时间旋转,只有需要运行的线程才能争夺CPU资源。

+2

当你输入快!我只是添加我的两分钱作为评论。 – AJG85

+0

所有评论对我来说都很棒:) @ Jason,所以thread是为程序设计的许多重要部分而做的,他们的工作不是? – Nazka

+0

线程可以用于任务...该任务可以是任何东西。它可能是运行整个程序的时间,例如网络守护进程,它将监听套接字上的连接,然后分离更多线程来管理这些连接。一个线程也可以用于一个小任务,比如解决一个高度并行任务的积分迭代。 – Jason

2

要回答第一个问题: 最好的方法是在您的代码中使用多线程技术,直到您达到甚至没有给您足够的收益为止。假设操作系统将处理委派给多个处理器(如果它们可用)。

如果您实际上正在处理多线程不够的问题,即使使用多个处理器(或者如果您在不使用多处理器的操作系统上运行),那么您也可能担心发现获得更多的权力。这可能意味着将整个网络产卵到其他机器。

我还没有使用过TBB,但我已经使用了IPP,并发现它是高效和精心设计的。 Boost是便携式的。

20

有一些重要的观点我认为应该加入@Jason的出色答案中。

首先,即使在单个处理器上,多线程并不总是假象 - 有些操作不涉及处理器。这些主要是I/O - 磁盘,网络终端等,用于这样的操作的基本形式是阻断同步,即程序等待,直到操作完成,然后前进。在等待时,CPU切换到另一个进程/线程。

,如果您有任何你可以在这段时间做(如后台计算,同时等待用户输入,服务其他请求等),您有两种基本选择:

  • 使用异步I/O :你打电话给非阻塞的 I/O提供一个回调函数,告诉它“在你完成时调用这个函数”。调用立即返回,I/O操作在后台继续。你继续与其他的东西。

  • 使用多线程:你有一个专用的线程为每种任务。当一个人等待阻塞I/O呼叫时,另一个继续。

这两种方法都是难以编程的范例,每种方法都有其优点和缺点。

  • 与异步I/O程序的逻辑逻辑不太明显,很难跟踪和调试。但是,您可以避免线程安全问题。
  • 带线程,挑战是编写thread-safe程序。线程安全缺陷是很难重现的令人讨厌的错误。过度使用锁定实际上会导致降级,而不是提高性能。

(即将到多处理)

多线程制成流行在Windows因为操纵处理是在Windows相当重(创建一个进程,上下文切换等),而不是其是更线程轻量级(至少在我使用Win2K时就是这种情况)。

在Linux/Unix上,进程更加轻量级。另外,Linux上的(AFAIK)线程实际上是作为一种内部进程实现的,因此线程与进程的上下文切换没有任何好处。但是,您需要使用某种形式的IPC(进程间通信),作为共享内存,管道,消息队列等。

在更简洁的笔记中,查看SQLite FAQ,其中声明“线程是邪恶的!:)

+2

还有第三个选项,通过select()/ poll()/ etc复用I/O。这比多线程更安全,比异步I/O更易于理解。 –

0

只是想提一下,基于流程的编程(http://www.jpaulmorrison.com/fbp)范例是一种自然的多程序/多处理的应用程序开发方法。它提供了从高级到低级的一致应用程序视图。 Java和C#实现利用了机器上的所有处理器,但较早的C++实现只使用一个处理器。但是,通过使用锁定连接,它可以很容易地扩展到使用BOOST(或者我假设的pthread)。我已经开始将其转换为使用光纤,但我不确定在这条路线上是否有任何意义。 :-)反馈将不胜感激。顺便说一句,Java和C#实现甚至可以使用套接字互相通信。