为啥 C# 中的多线程不能达到 100% CPU?
Posted
技术标签:
【中文标题】为啥 C# 中的多线程不能达到 100% CPU?【英文标题】:Why doesn't multithreading in C# reach 100% CPU?为什么 C# 中的多线程不能达到 100% CPU? 【发布时间】:2010-09-20 22:09:55 【问题描述】:我正在开发一个程序,它处理许多请求,但没有一个达到超过 50% 的 CPU(目前我正在处理双核)。所以我为每个请求创建了一个线程,整个过程更快。处理9个请求,单个线程持续02min08s,而3个线程同时工作时间减少到01min37s,但一直没有使用100%的CPU,只有50%左右。
如何让我的程序使用完整的处理器功能?
编辑 应用程序不受 IO 或内存限制,它们始终处于合理的水平。
我认为这与“双核”有关。
每个请求都使用一个锁定的方法调用,但它确实很快,我认为这不是问题。
我的代码中 CPU 开销更大的部分是通过 COM 调用 dll(从所有线程调用相同的外部方法)。这个 dll 也没有内存或 IO 限制,它是一个 AI 识别组件,我正在对薪水进行 OCR 识别,对请求进行薪水检查。
EDIT2
很可能STA COM Method是我的问题,为了解决这个问题我联系了组件所有者。
【问题讨论】:
【参考方案1】:您的应用程序中是否有大量锁定?如果线程之间的等待时间很长,这很容易解释。
除此之外(以及给出的其他答案),真的很难猜测。分析器是您的朋友...
编辑:好的,鉴于下面的 cmets,我认为我们正在做一些事情:
我的代码中 CPU 开销更大的部分是 通过 COM 调用 dll(同 从所有调用外部方法 线程)。
COM 方法是否会在 STA 中运行?如果是这样,它将只使用一个线程,序列化调用。我强烈怀疑这是它的关键。这类似于锁定该方法调用(诚然,不完全相同)。
【讨论】:
每个请求都会使用一个锁定的方法调用,但是确实很快,我不认为这是问题。 我的代码中 CPU 开销更大的部分是通过 COM 调用 dll(从所有线程调用相同的外部方法)。此 dll 也没有内存或 IO 限制。 不,您可以拥有可以从多个线程 (MTA) 调用的 COM 对象。 我支持 STA 诊断。听起来很有可能。 老实说,我不知道你会如何检查它——首先尝试资源管理器中的属性。至于改变它 - 你不能;如果它被设计为 STA,则更改它可能不安全。您必须询问原作者。【参考方案2】:问题出在 COM 对象上。
大多数 COM 对象在“单线程单元”的上下文中运行。 (您可能不时在 .NET 应用程序的 main 方法上看到 [STAThread] 注解?)
实际上,这意味着对该对象的所有分派都由单个线程处理。在问题上投入更多核心只会为您提供更多资源,让您可以坐下来等待或在 .NET 中做其他事情。
您可能想看看 Joe Duffy(Microsoft 的首席并行 .NET 人员)关于该主题的这篇文章。
http://www.bluebytesoftware.com/blog/PermaLink,guid,8c2fed10-75b2-416b-aabc-c18ce8fe2ed4.aspx
在实践中,如果您必须像这样针对单个 COM 对象执行大量操作,那么您将被淹没,因为 .NET 只会在您背后内部序列化访问模式。如果您可以创建多个 COM 对象并使用它们,那么您可以解决问题,因为每个对象都可以从不同的 STA 线程创建和访问。这将一直有效,直到您达到大约 100 个 STA 线程,然后事情就会变得不稳定。有关详细信息,请参阅文章。
【讨论】:
这是我在一些较旧的 PDF 库中发现的令人高兴的事情之一。 这也是你不敢在Web服务器上调用各种Excel.Application或Office Web Components的原因。突然之间,当你跨越 100 个线程时,它们开始翻转并共享全局变量,并从错误的线程中相互销毁,等等。【参考方案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.
【讨论】:
是的,很困惑,他们都在调用同一个实例,我会尝试为每个线程创建一个实例,谢谢。 我尝试为每个线程加载一个实例,这导致了 IO 绑定情况。 在此更改之前,运行大约需要 2 分钟,之后运行时间超过 3 分钟。 我想这取决于 COM 对象的作用。【参考方案5】:我想我也有类似的问题。我在 c# 中创建了多个线程,这些线程通过 COM 接口运行 c++ 代码。我的双核 CPU 从未达到 100%。
看完这篇文章,我几乎放弃了。然后我尝试在我的线程上调用 SetApartmentState(ApartmentState.STA)。
仅更改此设置后,CPU 已达到极限。
【讨论】:
【参考方案6】:听起来您的应用程序的性能可能不受可用 CPU 资源数量的“限制”。如果您正在通过网络处理请求,CPU 可能正在等待数据到达,或等待网络设备传输数据。或者,如果您需要查找数据来完成请求,cpu 可能正在等待磁盘。
【讨论】:
【参考方案7】:您确定您的任务需要密集的处理器活动吗?有IO处理吗?这可能是您的 50% 负载的原因。
测试: 尝试仅使用 2 个线程并为每个核心设置每个线程的亲和性。然后打开任务管理器,观察两个核心的负载情况。
【讨论】:
IO 处理很少,有几 KB。 我想我错了:(没有托管代码可以做到这一点,我发现的非托管代码似乎有问题。抱歉【参考方案8】:这不是一个真正的答案,但是您是否检查过 perfmon 以查看它正在使用哪些资源并在代码上运行分析器以查看它在哪里花费时间?
您如何确定 IO 或其他非 CPU 资源不是瓶颈?
你能简要描述一下线程在做什么吗?
【讨论】:
【参考方案9】:如果您的进程在 cpu 0 上运行并在那里产生线程,那么它将达到的最大值为 50%。查看您是否在两个内核上或仅在一个内核上运行线程。我冒昧地猜测您被隔离到单个核心,或者您的一个依赖资源被锁定在单个核心上。如果它恰好达到 50%,那么单核很可能会成为您的瓶颈。
【讨论】:
我在这个项目的主线程上有一个以前的代码,它使项目需要大约 100% 的处理,这是一个导致 while-true 条件的代码。当然我修复了代码,因为它消耗资源并且是错误的,但它显示我的项目可以运行在 100%【参考方案10】:所以你解决了使用单个 COM 对象的问题,现在遇到了 IO 问题。
多线程运行时间增加可能是因为将随机 IO 混合在一起,这会减慢速度。
如果数据集适合 RAM,请尝试查看是否可以将其预取到缓存中。可能只是读取数据,也可能是内存映射与命令一起使其可用。
这就是为什么 SQL 数据库通常会在您意想不到的查询上选择顺序表扫描而不是索引扫描的原因:按顺序读取所有数据要比按随机块读取要快得多。
【讨论】:
【参考方案11】:也许我误解了什么,但你说你的请求(每个请求都在一个单独的线程中)都没有达到 100% 的 CPU。
您使用的是什么操作系统?
我似乎隐约记得,在旧版本的 Windows(例如,早期的 XP 和 2000 年代)中,CPU 利用率是从总共两个处理器中考虑的,因此单个线程无法使其超过 50%,除非它是空闲进程..
【讨论】:
我使用的是 Windows XP SP2。但是当我在“主”线程出现一段时间的真实情况时,我实际上可以达到 100%。 COM STA 的问题很可能是我的问题,我联系了组件所有者;)【参考方案12】:另外请注意,您是否尝试过不从 Visual Studio 启动代码(无论发布/调试设置如何)?
【讨论】:
我在 VS05 的调试模式下运行,它的二进制文件来自资源管理器。【参考方案13】:问题在于 COM 对象。它是 STA,我不能在同一个进程上同时运行两个实例。当我为 COM 类创建一个实例时,另一个变得不可用。
我已经联系了组件开发人员,他们正在考虑可以为我做些什么。
谢谢大家;)
【讨论】:
以上是关于为啥 C# 中的多线程不能达到 100% CPU?的主要内容,如果未能解决你的问题,请参考以下文章
我的多线程游戏一直处于 100% CPU。如何管理线程活动以减少 CPU 负载?
为啥用 Regex.IsMatch 检查这个字符串会导致 CPU 达到 100%?