【问题标题】:Multithreading on a multi core machines not maxing CPU多核机器上的多线程没有最大化 CPU
【发布时间】:2009-07-14 18:00:05
【问题描述】:

我正在通过两种方法维护他人使用多线程的代码:

1: ThreadPool.QueueUserWorkItem(New WaitCallback(AddressOf ReadData), objUpdateItem)

2: Dim aThread As New Thread(AddressOf LoadCache)
   aThread.Start()

但是,在双核机器上,CPU 利用率只有 50%,而在启用超线程的双核机器上,CPU 利用率只有 25%。

显然线程非常复杂,但这种行为似乎表明我没有理解一些简单的基本事实?

更新

不幸的是,代码太复杂了,无法在此处发布,但出于参考目的,大致是这样的……我有大约 500 个帐户,其数据从数据库加载到内存缓存中……每个帐户单独加载,并且该进程首先调用一个长时间运行的存储过程,然后对返回的数据进行操作和缓存。所以,在这种情况下线程化的重点是确实有一个瓶颈击中数据库(即:线程将空闲长达 30 秒等待查询返回),所以我们线程允许其他人开始处理他们从 Oracle 收到的数据。

所以,主线程执行:

ThreadPool.QueueUserWorkItem(New WaitCallback(AddressOf ReadData), objUpdateItem) 

然后,ReadData() 继续执行(恰好一次):

Dim aThread As New Thread(AddressOf LoadCache)
aThread.Start()

这发生在递归函数中,因此 QueueUserWorkItem 可以执行多次,然后通过 aThread.Start 执行恰好一个新线程

希望这可以让您对事情的发生方式有一个不错的了解。

那么,在这种情况下,理论上这不应该固定两个内核,而不是在一个内核上达到 100% 的最大值,而另一个内核基本上处于空闲状态?

【问题讨论】:

  • 如果不知道 ReadData 和/或 LoadCache 方法在做什么,我们无法真正回答。
  • 验证 ReadData 和 LoadCache 使用 CPU 的时间是否足以进行测量。如果任一方法等待某些资源(例如,文件系统中的文件、WaitHandle 或锁),它不会使用任何 CPU,因为线程处于等待模式。
  • 我不知道你的程序在做什么,但是如果你不能为你的程序设置一个好的级别,有时最大化你的程序的 cpu 使用是一个非常烦人的行为,因为这会导致程序运行时完全阻塞的机器。有时在等待长时间计算的结果时,有人喜欢至少留下一个核心来浏览或玩纸牌;)

标签: c# .net vb.net multithreading


【解决方案1】:

该代码启动一个线程,该线程将执行某项操作。要让多个核心工作,您需要启动多个线程并让它们都忙起来。启动一个线程来做一些工作,然后让你的主线程等待它不会更快地完成任务。在后台线程上启动一个长时间运行的任务是很常见的,以便 UI 保持响应,这可能是这段代码的目的,但它不会让任务更快地完成。

@Judah Himango - 我曾假设这两行代码是如何在程序的两个不同位置实现多线程的示例。也许OP可以澄清是否是这种情况,或者这两行是否真的在一种方法中。如果它们是一种方法的一部分,那么我们需要看看这两种方法实际上在做什么。

更新:
听起来确实应该最大限度地利用两个核心。递归调用 ReadData() 是什么意思?如果每个新线程仅在其末尾或附近调用 ReadData 以启动下一个线程,那么这可以解释您所看到的行为。
我不确定这实际上是一个好主意。如果存储过程需要 30 秒来获取数据,那么它可能会在数据库服务器上施加相当大的负载。并行运行 500 次只会让事情变得更糟。显然我不知道您的数据库或数据,但我会考虑提高存储过程的性能。
如果多线程确实看起来像前进的方向,那么我将在主线程上为每个需要加载的帐户调用一次 ThreadPool.QueueUserWorkItem 循环。我还将删除显式线程创建并仅使用线程池。这样,您就不太可能因创建过多线程而导致本地机器饿死。

【讨论】:

  • 代码启动了2个线程,对吧?一个线程池线程,另一个用户创建的线程。
  • 我添加了一些附加信息,希望说明正在发生的事情。
  • 足够接近......这个问题有点太模糊了,没有一个真正“正确”的答案! :)
【解决方案2】:

你正在旋转多少个线程?它可能看起来很原始(等待几年,您将不再需要这样做),但您的代码必须确定启动的最佳线程数,并启动那么多线程。简单地运行单个线程不会使事情变得更快,也不会固定物理处理器,尽管出于其他原因(例如,保持 UI 响应的工作线程)可能会很好。

在许多情况下,您会希望运行的线程数等于您可用的逻辑内核数(我相信可以从 Environment.ProcessorCount 获得),但它可能还有其他基础。例如,当我受到远程进程延迟的限制时,我已经启动了几十个线程,与不同的主机通信。

【讨论】:

  • 根据 Joe Duffy 的说法,您的代码永远不应该确定启动的最佳线程数。相反,请使用 ThreadPool,它将在您正在工作的机器上为您做出最佳选择。 Environment.ProcessorCount 看起来像是正确的工具,但它是从环境中读取出来的,可以写入也可以读取。
  • 你有参考资料吗,底座?
  • WRT Environment.ProcessorCount,不管文档描述了什么,该属性使用 kernel32 函数 GetSystemInfo 来发现逻辑处理器的数量。
  • 参考:PDC,2008。您可能还会看到他的书 Concurrent Programming on Windows。
  • 嗯。我了解他的来历,使用 ThreadPool 是正确的,但是(假设 bluebytesoftware.com/blog/… 是他论点的一个很好的总结),我认为他过于简单化了。特别是,他似乎认为,使线程数量明显大于现有内核数量是一个坏主意。并非总是如此。瓶颈并不总是处理能力;实际上,它可能与本地机器无关。
【解决方案3】:

多线程和多核是两个不同的东西。多线程做事通常不会为您带来巨大的性能提升,有时恰恰相反。操作系统可能会采取一些技巧将 CPU 周期分布​​到多个内核上,但这就是它的终点。

您正在寻找的是并行性。 .NET 4.0 框架将添加许多新功能来支持并行。在这里先睹为快:
http://www.danielmoth.com/Blog/2009/01/parallelising-loops-in-net-4.html

【讨论】:

    【解决方案4】:

    CPU 行为表明应用程序仅使用一个逻辑处理器。 50% 将是 2 个中的一个(proc+proc)。 25% 将是 4 个逻辑处理器中的一个(proc + HT + proc + HT)

    【讨论】:

      【解决方案5】:

      您总共有多少个线程,您在 LoadCache 中是否有任何锁。 SyncLock 可以在多线程系统中充当单线程(通过设计)。此外,如果您只假脱机一个线程,您将只获得一个工作线程。

      【讨论】:

        【解决方案6】:

        CPU 利用率表明您只使用一个内核;这可能表明您已将线程添加到无益的部分(在这种情况下,CPU 时间不是瓶颈)。

        如果加载缓存或读取数据的速度非常快,多线程不会大幅提高速度性能。同样,如果您遇到不同的瓶颈(服务器带宽慢等),它可能不会显示为 CPU 使用率。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2012-02-07
          • 1970-01-01
          • 2011-03-09
          • 1970-01-01
          • 2010-12-16
          • 2014-02-20
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多