C# 关于多线程和CPU的问题

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了C# 关于多线程和CPU的问题相关的知识,希望对你有一定的参考价值。

使用1个线程进行操作耗时100秒

为什么使用2个线程和使用20个线程都一样只节省一半时间

照理说20个线程不是应该更省时间吗? 求解...

1.CPU发展趋势
核心数目依旧会越来越多,依据摩尔定律,由于单个核心性能提升有着严重的瓶颈问题,普通的桌面PC有望在2017年末2018年初达到24核心(或者16核32线程),我们如何来面对这突如其来的核心数目的增加?编程也要与时俱进。笔者斗胆预测,CPU各个核心之间的片内总线将会采用4路组相连:),因为全相连太过复杂,单总线又不够给力。而且应该是非对称多核处理器,可能其中会混杂几个DSP处理器或流处理器。

2.多线程与并行计算的区别
(1)多线程的作用不只是用作并行计算,他还有很多很有益的作用。
还在单核时代,多线程就有很广泛的应用,这时候多线程大多用于降低阻塞(意思是类似于
while(1)

if(flag==1)
break;
sleep(1);

这样的代码)带来的CPU资源闲置,注意这里没有浪费CPU资源,去掉sleep(1)就是纯浪费了。
阻塞在什么时候发生呢?一般是等待IO操作(磁盘,数据库,网络等等)。此时如果单线程,CPU会干转不干实事(与本程序无关的事情都算不干实事,因为执行其他程序对我来说没意义),效率低下(针对这个程序而言),例如一个IO操作要耗时10毫秒,CPU就会被阻塞接近10毫秒,这是何等的浪费啊!要知道CPU是数着纳秒过日子的。
所以这种耗时的IO操作就用一个线程Thread去代为执行,创建这个线程的函数(代码)部分不会被IO操作阻塞,继续干这个程序中其他的事情,而不是干等待(或者去执行其他程序)。

同样在这个单核时代,多线程的这个消除阻塞的作用还可以叫做“并发”,这和并行是有着本质的不同的。并发是“伪并行”,看似并行,而实际上还是一个CPU在执行一切事物,只是切换的太快,我们没法察觉罢了。例如基于UI的程序(俗话说就是图形界面),如果你点一个按钮触发的事件需要执行10秒钟,那么这个程序就会假死,因为程序在忙着执行,没空搭理用户的其他操作;而如果你把这个按钮触发的函数赋给一个线程,然后启动线程去执行,那么程序就不会假死,继续相应用户的其他操作。但是,随之而来的就是线程的互斥和同步、死锁等问题,详细见有关文献。
现在是多核时代了,这种线程的互斥和同步问题是更加严峻的,单核时代大都算并发,多核时代真的就大为不同,为什么呢?具体细节请参考有关文献。我这里简单解释一下,以前volatile型变量的使用可以解决大部分问题,例如多个线程共同访问一个Flag标志位,如果是单核并发,基本不会出问题(P.S.在什么情况下会出问题呢?Flag有多个,或者是一个数组,这时候只能通过逻辑手段搞定这个问题了,多来几次空转无所谓,别出致命问题就行),因为CPU只有一个,同时访问这个标志位的只能有一个线程,而多核情况下就不太一样了,所以仅仅volatile不太能解决问题,这就要用到具体语言,具体环境中的“信号量”了,Mutex,Monitor,Lock等等,这些类都操作了硬件上的“关中断”,达到“原语”效果,对临界区的访问不被打断的效果,具体就不解释了,读者可以看看《现代操作系统》。
(2)并行计算还可以通过其他手段来获得,而多线程只是其中之一。
其他手段包括:多进程(这又包括共享存储区的和分布式多机,以及混合式的),指令级并行。
ILP(指令级并行),x86架构里叫SMT(同时多线程),在MIPS架构里与之对应的是super scalar(超标量)和乱序执行,二者有区别,但共同点都是可以达到指令级并行,这是用户没法控制的,不属于编程范围,只能做些有限的优化,而这有限的优化可能只属于编译器管辖的范畴,用户能做的甚少。
(3)典型的适于并行计算的语言
Erlang和MPI:这两个前者是语言,后者是C++和Fortran的扩展库,效果是一样的,利用多进程实现并行计算,Erlang是共享存储区的,MPI是混合型的。
C#.NET4.0:新版本4.0可以用少量代码实现并行For循环,之前版本需要用很繁琐的代码才能实现同样功能。这是利用了多线程实现并行计算。Java和C#3.5都有线程池(ThreadPool),也是不错的很好用的多线程管理类,可以方便高效的使用多线程。
CUDA,还是个初生牛犊,有很大的发展潜力,只不过就目前其应用领域很有限。其目前只能使用C语言,而且还不是C99,比较低级,不能使用函数指针。个人感觉这由于硬件上天生的局限性(平均每个核心可用内存小,与系统内存通讯时间长),只适用于做科学计算,静态图像处理,视频编码解码,其他领域,还不如高端CPU。等以后GPU有操作系统了,能充分调度GPU资源了,GPU就可以当大神了。游戏中的物理加速,实际上多核CPU也能很好的做到。
参考技术A 理论上来说你的理解是对的 但是要看软件啊 对多线程优化好的软件 那是相当明显的比如说我用的3DSMAX 它可以运用多线程 并且可以让CPU100%满载运行 那么 i7 有八个线程 和i5 4个线程
就是我用这个软件就可以提升50%左右 向至强的CPU就更强了 主要是软件 软件可以使用多线程那么提升是相当明显的!追问

请问一般都是怎么优化的,能举个例子吗?

还有下面这段代码,为什么线程数调高了,速度反而更慢 - -!

 

追答

你在百度百科上面 看看多线程 就明白了 上面写的 很详细的!

本回答被提问者和网友采纳
参考技术B 线程中间有个sleep方法。
程序中有死循环而且中间没用sleep所有导致的。循环一次一定要sleep一下。要不他一直占住CPU不释放,导致程序死锁。
在线程里面加上 :
System.Threading.Thread.Sleep(10000);就能解决这个问题了。

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

【中文标题】为啥 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# 关于多线程和CPU的问题的主要内容,如果未能解决你的问题,请参考以下文章

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

关于C#多线程易失域锁的分享

关于多线程

C# socket服务器端 多线程客户端 如何少量使用CPU

关于C#的多线程方面

C#多线程与异步的区别