为什么C#中的多线程不能达到100%的CPU?

时间:2008-11-05 17:19:09

标签: c# .net multithreading multicore

我正在开发一个处理许多请求的程序,它们都没有超过50%的CPU(目前我正在研究双核)。所以我为每个请求创建了一个线程,整个过程更快。处理9个请求,单个线程持续02min08s,同时3个线程同时工作,时间减少到01min37s,但它不使用100%CPU,只有大约50%。

我怎样才能让我的程序使用完整的处理器功能?

修改 应用程序不是IO或内存有限,它们一直处于合理的水平。

我认为这与'双核'事物有关。

每个请求都使用一个锁定的方法调用,但它确实很快,我不认为这是问题所在。

我的代码中代价更高的部分是通过COM调用dll(从所有线程调用相同的外部方法)。这个dll也没有内存或IO限制,它是一个AI识别组件,我正在对薪水进行OCR识别,这是对请求的薪水。

EDIT2

STA COM方法很可能是我的问题,我联系了组件所有者以解决这个问题。

13 个答案:

答案 0 :(得分:25)

您的申请中是否有重大锁定?如果线程相互等待很多,那很容易解释它。

除此之外(以及给出的其他答案),真的很难猜到。探查器是你的朋友......

编辑:好的,鉴于以下评论,我认为我们正在做点什么:

  

我的代码中代价更高的部分是   通过COM调用dll(同样的   从所有人调用外部方法   线程)。

COM方法是否在STA中运行?如果是这样,它将只使用一个线程,序列化调用。我强烈怀疑这是关键。它类似于锁定该方法调用(不可否认,并不完全相同)。

答案 1 :(得分:17)

问题是COM对象。

大多数COM对象在“单线程单元”的上下文中运行。 (您可能不时在.NET应用程序的main方法上看到[STAThread]注释?)

实际上,这意味着对该对象的所有调度都由单个线程处理。在问题上投入更多内核只会为您提供更多资源,可以在.NET中等待或执行其他操作。

你可能想看一下Joe Duffy(微软的并行.NET主管)关于这个主题的这篇文章。

http://www.bluebytesoftware.com/blog/PermaLink,guid,8c2fed10-75b2-416b-aabc-c18ce8fe2ed4.aspx

在实践中,如果你必须针对像这样的单个COM对象做一些事情,那么你就会受到冲击,因为.NET只会在你的背后内部序列化访问模式。如果您可以创建多个COM对象并使用它们,那么您可以解决该问题,因为可以从不同的STA线程创建和访问每个对象。这将一直有效,直到你达到大约100个STA线程,然后事情会变得很糟糕。有关详细信息,请参阅文章。

答案 2 :(得分:13)

处理器可能不再是完成流程的瓶颈。瓶颈可能转移到磁盘访问,网络访问或内存访问。你也可能遇到线程争夺锁定的情况。

只有你确切地知道你的线程在做什么,所以你需要考虑到上面的内容。

答案 3 :(得分:4)

这取决于你的程序做什么 - 你的并发请求所执行的工作可能是IO限制的 - 受到(例如)硬盘速度的限制 - 而不是CPU限制,当你看到你的CPU达到100%时。

编辑之后,听起来像COM STA对象可能是罪魁祸首。

所有线程都调用COM对象的同一个实例吗?是否可以使您的工作线程STA线程,并在每个线程上创建COM对象的单独实例。通过这种方式,可以避免STA瓶颈。

判断COM coclass是否为STA:

class Test
{
  static void Main() //This will be an MTA thread by default
  {
    var o = new COMObjectClass();
    // Did a new thread pop into existence when that line was executed?
    // If so, .NET created an STA thread for it to live in.
  }
}

答案 4 :(得分:2)

我想我遇到了类似的问题。我在c#中创建多个线程,通过COM接口运行c ++代码。我的双核CPU从未达到100%。

看完这篇文章后,我几乎放弃了。然后我尝试在我的线程上调用SetApartmentState(ApartmentState.STA)。

仅更改此项后,CPU最大化。

答案 5 :(得分:0)

听起来你的应用程序的性能可能不受可用cpu资源量的“约束”。如果您正在通过网络处理请求,则cpu可能正在等待数据到达,或者网络设备要传输数据。或者,如果您需要查找数据以满足请求,则cpu可能正在等待磁盘。

答案 6 :(得分:0)

您确定您的任务需要密集的处理器活动吗?有没有IO处理?这可能是您50%负载的原因。

测试: 尝试仅使用2个线程,并为每个Core设置每个线程的亲和力。然后打开任务管理器并观察两个核心的负载。

答案 7 :(得分:0)

这不是一个真正的答案,但您是否检查过perfmon以查看它正在使用哪些资源,并让您在代码上运行分析器以查看它花费的时间?

您如何确定IO或其他非CPU资源不是瓶颈?

你能简单介绍一下线程在做什么吗?

答案 8 :(得分:0)

如果您的进程在cpu 0上运行并在那里生成线程,那么它将达到的最大值为50%。查看是否在两个核心上或仅在一个核心上运行线程。我冒昧地猜测你被孤立到一个核心,或者你的一个依赖资源被锁定在一个核心上。如果它恰好达到50%,那么单个核心很可能成为你的瓶颈。

答案 9 :(得分:0)

所以你解决了使用单个COM对象的问题,现在出现了IO问题。

多个线程的运行时间增加可能是因为将随机IO混合在一起,这会降低它的速度。

如果数据集适合RAM,请尝试查看是否可以将其预取到缓存中。也许只是读取数据,或者可能是内存将它与命令一起映射以使其可用。

这就是为什么SQL数据库通常会在您不期望的查询上通过索引扫描选择顺序表扫描:按顺序读取所有数据要快得多,而不是以随机块的形式读取它。

答案 10 :(得分:0)

也许我误解了一些东西,但是你说没有你的请求(每个都在一个单独的线程中)达到100%的CPU。

您使用的是哪种操作系统?

我似乎模糊地回忆起在旧版本的Windows(例如早期的XP和2000s)中,CPU利用率被认为是总共两个处理器,因此单个线程无法使其超过50%,除非它是闲置过程..

答案 11 :(得分:0)

还有一点需要注意,您是否尝试过不是从Visual Studio启动代码(无论发布/调试设置如何)?

答案 12 :(得分:0)

问题是COM对象。它是STA,我不能在同一进程上同时运行两个实例。当我为COM类创建实例时,另一个实例变得无法使用。

我联系了组件开发人员,他们正在考虑他们能为我做些什么。

谢谢大家;)