【问题标题】:How many threads to create?要创建多少个线程?
【发布时间】:2010-12-19 17:48:23
【问题描述】:

我现在正在学习如何编写多线程程序,我有一个假设性的问题,即一个程序有多少线程是最佳的。

让我描述两个场景。

第一种情况是我有一个很容易多线程的程序,但每个线程都会做很多工作(每个线程的执行时间大约为秒)。

第二种情况是我有一个程序也很容易多线程,但每个线程的执行时间非常短,大​​约为毫秒。

在任何一种情况下,多线程程序的最有效方法是什么?是在我的系统内存允许的范围内创建尽可能多的线程,还是在创建新线程之前等待线程完成,这样我每次最多只能运行 4 个工作线程。

一方面,许多线程可能存在内核在线程之间切换的开销问题(据我了解,这不是那么严重的开销)。另一方面,如果我限制运行的线程数,这意味着我将运行额外的检查条件并锁定和解锁计数器变量以跟踪正在运行的线程数,并在旧线程完成时创建新线程.

我可以看到,如果有很多小线程,最好简单地用尽可能多的线程重载我的系统,因为在线程完成运行之前不会有太多的线程切换。这将节省我不断跟踪线程数的开销。

另外,如果只有几个大线程(少数,我的意思是几百个左右的大线程),那么跟踪线程是有意义的,这样我们就可以将线程保持在最佳数量,这样就不会出现非常多的线程切换(因为开销会更大,因为我们可能会在单个线程完成之前切换很多次)。

那么这些假设对于每种情况是否都是正确的,或者是否存在一种在所有情况下都正确的通用方法?

注意:这是假设一个多核系统(现在,让我们忽略超线程)并忽略与多线程相关的任何典型问题(假设所有线程都有私有写入位置,并且只能从公共写入位置读取,锁定和只有在增加或减少活动线程数的计数器时才会解锁)。

谢谢,

-伪造

【问题讨论】:

    标签: c++ multithreading optimization


    【解决方案1】:

    场景 #1:创建 n 个线程,其中 'n' 是 CPU 内核数

    场景 #2:相同,但不是一直创建和终止线程,而是使用基于工作项/线程池的方法,就像 .NET Parallel Framework 所做的那样。

    编辑:这是一篇涵盖#2 - http://msdn.microsoft.com/en-us/magazine/cc163340.aspx 的好文章;让 PFx 算出要运行多少个线程,你只需要描述任务之间是如何相互关联的。

    【讨论】:

    • 好点。如果任务要运行几微秒,那么设置线程并使其运行所需的时间几乎与实际工作一样长!
    • 此外,如果您使用 C++ 和 Visual Studio 2010,则可以使用并行模式库和并发运行时(PFX 是 .NET)。有关指向 .NET 和 C++ 代码的指针,请参阅并发中心:msdn.microsoft.com/en-us/concurrency/default.aspx
    【解决方案2】:

    通常的方法是使线程数可配置,并跨多个配置分析应用程序性能。

    另外请注意,在许多情况下,导致多线程应用程序效率低下的不是与许多线程或上下文切换相关的开销,而是由同步访问共享资源引起的瓶颈。即使您假设您的代码是防死锁的,但如果有大量 IO 正在进行,那么糟糕的同步实现可能会有效地扼杀您的并行化本来可以为您带来的任何好处。

    【讨论】:

    • 如果每个线程都在读取全局信息,然后将某些内容直接吐出到硬盘上会怎样?我的瓶颈将是硬盘驱动器写入序列对吗?在这种情况下,如果多个线程请求写入序列(每个文件最多只有几 KB)会发生什么情况,HD 会在写入文件之前跳来跳去还是在继续之前完成一个文件?
    • 取决于驱动器。大多数硬盘驱动程序(最初只有 SCSI,但现在也支持 ATA)支持分散-收集操作,其中请求组在内部重新排序以匹配驱动器磁头的移动。
    • 另外,在进行任何过早的优化之前,使用操作系统性能计数器来确定瓶颈的实际位置。
    • 此外,所有现代操作系统都支持写入缓存。很有可能,如果每个线程只写入几 KB,大部分写入将缓存在内存中,您不必担心硬盘驱动器延迟。如果线程需要写入同一个文件,您可能会遇到问题,因为即使写入速度很快,您也只能提供对它的串行访问 - 如果许多线程正在运行或写入频繁,可能会导致瓶颈.
    【解决方案3】:

    这不是一个有固定答案的问题,但有几点:

    由于您的线程寿命很短,也许您应该考虑使用池来管理它们?您可以创建一个包含多个线程的池,这些线程适合主机系统和任务配置文件(比如每个内核开始一个),并在某种队列上提供它的工作。通过这样做,您可以消除启动新线程、为每个任务分配堆栈等的开销。

    至于池的适当线程数,这取决于您正在运行的任务类型。如果它们是 CPU 绑定的任务,那么每个 CPU 一个线程是一个很好的选择:当你不需要时避免上下文切换。另一方面,如果它们是 IO 绑定任务,比如线程进行套接字通信,那么您可能希望将这个数字翻倍,以便在等待 IO 输入时更好地利用处理器。

    无论如何,简而言之,对于这类东西,没有一种万能的方法。与以往一样,分析您的代码运行情况,找出效率低下的地方,并根据您的结果对其进行调整。

    【讨论】:

      【解决方案4】:

      假设您指的是 Windows 程序,即使它是 C++ 而不是 dot-Net 程序,在开始之前浏览 Joe Duffy 的“Windows 上的并发编程”也会得到回报。他对使用 Windows 提供的线程池例程进行了很好的宣传,最令人信服的是,它们已经在内部针对处理器配置进行了调整,从而减轻了您的负担。
      如果你继续自己动手,整本书讨论的问题肯定会让你免于被标准陷阱绊倒。

      【讨论】:

        【解决方案5】:

        我会从一个足够好的数字开始,然后收集统计数据以找出要运行的正确线程数以实现良好的性能。

        【讨论】:

        • 取决于运行它的 PC,不是吗?
        • 是的,当然是环境,工作线程的类型会解决所有问题
        【解决方案6】:

        线程并不便宜。我基本上知道使用它们的两个原因:

        1. 让多个硬件并行工作,无论它们是 CPU 内核、磁盘磁头、其他类型的机器还是世界另一端的服务器。

        2. 让多人同时工作,就像用户拥有自己的会话一样。这里的优势不是速度,而是易于编码每个用户的交互序列。

        或两者兼而有之,例如,如果您有一个线程要处理,一个线程要响应用户。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2023-02-11
          • 1970-01-01
          • 2015-12-06
          • 1970-01-01
          • 1970-01-01
          • 2013-02-24
          • 1970-01-01
          • 2012-01-04
          相关资源
          最近更新 更多