【问题标题】:Multithreading vs multiprocessing多线程与多处理
【发布时间】:2011-09-17 07:09:26
【问题描述】:

我是这种编程的新手,需要你的观点。

我必须构建一个应用程序,但我无法让它足够快地计算。我已经尝试过 Intel TBB,而且很好用,但是我没有使用过其他库。

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

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

【问题讨论】:

  • 在多处理器机器中使用像 boost 这样的线程库将利用可用的内核。您还可以在一个处理器上拥有多个线程,这些线程可以与 Pentium 4 中的超线程等技术交错。您的意思是多进程和分布式系统还是多线程?您实际上可以同时执行这两种操作,但线程的本质是获得并行性,而与处理器无关。据我所知,FYI boost 是基于 posix 线程的,我发现它很容易使用。此外,新的 C++0x 标准将包括该语言原生的线程支持。
  • 您如何看待 Boost 和 Intel TBB?它比英特尔 TBB 快吗?或者如果我有英特尔处理器,我应该选择英特尔?
  • 英特尔 TBB 在某些领域具有优势,它提供了优化的并行原语,如并行 for 循环等,有助于在英特尔处理器上创建并行分散-聚集式算法,以及其他并行计算。 Boost threads 主要是一个跨平台的线程包,可以在多种硬件和操作系统平台上运行。但是,如果您需要某些并行计算原语,Boost 线程不会直接提供,您必须自己编写代码。因此,您可以将 TBB 视为比 Boost 更高级别的抽象。
  • 从未使用过英特尔 TBB,所以不能说,但您的性能主要取决于您的实施。我为跨平台性质选择了 boost,以便异步任务服务器可以分布在 linux 或 windows 机器上。我更喜欢 boost::thread 而不是 Windows 线程,或者 QThread 这是我使用过的唯一其他线程。 TBB wiki 文章使它听起来比 boost 更高级别,因此最初可能更容易实现,具体取决于您正在寻找的线程控制级别:en.wikipedia.org/wiki/Intel_Threading_Building_Blocks
  • 一个重要的区别:当线程崩溃时,进程停止。当一个线程挂起时,要么进程停止,要么你杀死它,你必须担心内存的状态。这意味着多处理可以更加健壮。例如,对于服务器来说,至少有一个与实际执行的进程分开的监控/启动进程是“正常的”。

标签: c++ multithreading parallel-processing multiprocessing


【解决方案1】:

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

在单处理器系统上,当运行多个线程时,实际观察到计算机同时执行多项操作(即多任务)是一种错觉,因为在幕后真正发生的是在单个 CPU 上执行时间分片的软件调度程序。因此,在任何给定时间只发生了一个任务,但调度程序在任务之间切换的速度足够快,因此您永远不会注意到有多个进程、线程等在争夺相同的 CPU 资源。

在多处理器系统上,时间片的需求减少了。时间片效应仍然存在,因为现代操作系统可能有数百个线程竞争两个或更多处理器,并且线程数与可用处理核心数之间通常没有一对一的关系。所以在某些时候,一个线程必须停止,另一个线程在两个线程共享的 CPU 上启动。这再次由操作系统的调度程序处理。话虽如此,对于多处理器系统,您可以同时发生两件事,这与单处理器系统不同。

最后,从某种意义上说,这两个范例实际上有些正交,当您想要两个或更多任务异步运行时,您将需要多线程,但由于时间分片,您不一定需要多处理器系统来实现这一点。如果您尝试运行多个线程,并且正在执行高度并行的任务(即尝试解决积分),那么是的,您可以在问题上投入的核心越多越好。您不一定需要线程和处理核心之间的一对一关系,但同时,您不希望分拆太多线程,最终导致大量空闲线程,因为它们必须等待被安排在可用的 CPU 内核之一上。另一方面,如果您的并行任务需要一些顺序组件,即一个线程将等待另一个线程的结果才能继续,那么您可以使用某种类型的屏障或同步方法运行更多线程,所以需要空闲的线程没有使用 CPU 时间旋转,只有需要运行的线程在竞争 CPU 资源。

【讨论】:

  • 当你打字快!我将添加我的两分钱作为评论。
  • 所有评论对我来说都很棒 :) @Jason,所以线程旨在对应用程序的许多重要部分进行编程并完成他们的工作吗?
  • 一个线程可以用于一个任务......这个任务可以是任何东西。它可能是在你的程序的整个持续时间内运行的东西,例如一个网络守护进程,它会“监听”套接字上的连接,然后分出更多线程来管理这些连接。线程也可以用于小任务,例如求解积分的迭代,这是一项高度并行的任务。
  • 所以你在程序中的任何地方都使用多线程?我什么时候应该停止在线程中划分我的程序?使用 multiproc 库进行大计算不是更好吗(Intel TBB、OpenMP..)?
  • 这些类型的库可以帮助完成计算任务,因为它们是对原始线程库(如 Boost、pthreads 等)的更高级别的抽象,但是当您需要同时执行多项操作时时间”(我用引号说,因为在单处理器系统上这是一种错觉),每个任务都需要一个单独的线程。很多时候,您编写的实用程序只做一件事,因此您不需要用于这些类型的应用程序的线程。例如,grepsedcp 等命令行应用程序不是多线程的。
【解决方案2】:

我认为应该在@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,它声明“线程是邪恶的”! :)

【讨论】:

  • 还有第三种选择,通过 select()/poll()/etc 复用 I/O。这比多线程更安全,也比异步 I/O 更容易理解。
【解决方案3】:

回答第一个问题: 最好的方法是在你的代码中使用多线程技术,直到你达到即使这样也不能给你带来足够好处的地步。假设操作系统将处理对多个处理器的委托(如果它们可用)。

如果您实际上正在解决多线程不够用的问题,即使使用多个处理器(或者如果您在不使用多个处理器的操作系统上运行),那么您可以担心发现如何获得更多的权力。这可能意味着通过网络向其他机器生成进程。

我没有使用过 TBB,但我使用过 IPP,发现它高效且设计良好。 Boost 是便携式的。

【讨论】:

    【解决方案4】:

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2019-07-14
      • 1970-01-01
      • 2020-05-18
      • 2019-09-04
      • 2022-08-16
      • 1970-01-01
      • 2019-11-27
      相关资源
      最近更新 更多