[英]Threading example C#
我在网上找到了这本书。 Joseph 的 C# 线程。 我试过它的例子
class ThreadTest
{
static void Main()
{
Thread t = new Thread(WriteY); // Kick off a new thread
t.Start(); // running WriteY()
// Simultaneously, do something on the main thread.
for (int i = 0; i < 10000000; i++) Console.Write("x");
Console.ReadLine();
}
static void WriteY()
{
for (int i = 0; i < 10000000; i++) Console.Write("y");
}
}
问题是,当我运行这个程序时(我在 for 循环中给出了更高的值来观察)我的 CPU 利用率坚持 100%。 我不想要这个,我的意思是,无论如何要减少以减少这个程序的 CPU 密集度? 我只是多线程概念的新手,所以我想我应该提前询问。
如果您可以同时使用多个资源,则多线程可以改善您的应用程序。 例如,如果您有多个核心或多个CPU,我相信上面的示例应该会更好。
或者,如果您有一个使用CPU的线程,以及另一个同时使用该磁盘的线程,如果您使用多线程,它也会表现更好。
但是,如果您有一个CPU或一个单核,则上面的示例将无法更好地执行。 它会表现得更糟。
你无法降低利用率,因为你使用的是两个线程(很可能是在双核上),这两个线程都是工作密集型的(它们循环并打印一些东西)。 也许减少线程优先级可能有所帮助,但我认为这不是这个例子的重点。
WriteY函数中的循环将尽快执行。 所以它将使用100%的CPU。 如果您希望它不那么耗费资源,那么您可以做两件事:
更改线程的优先级。 这样,您的应用程序仍将使用100%的CPU,但如果另一个线程需要CPU资源,该线程将“减速”
在WriteY函数中添加暂停:
static void WriteY() { for (int i = 0; i < 10000000; i++) { Console.Write("y"); Thread.Sleep(100); } }
在Console.Write
之后添加Thread.Sleep(num of millseconds)
因为循环将完全利用cpu。
class ThreadTest
{
static void Main()
{
Thread t = new Thread(WriteY);
// Kick off a new thread
t.Start();
// running WriteY()
// Simultaneously, do something on the main thread.
for (int i = 0; i < 10000000; i++)
{
Console.Write("x");
Console.ReadLine();
}
}
static void WriteY()
{
for (int i = 0; i < 10000000; i++)
{
Console.Write("y");
Thread.Sleep(1000); // let the thread `sleep` for one seconds before running.
}
}
}
更新
好吧,你可以使用这个例子 ,如果你有多个核心,使用.Net 4 Parallel Extensions。
var result = from ipaddress in new[]
{
"111.11.11.11",
"22.22.22.22",
"22.33.44.55"
/* or pulled from whatever source */
}
.AsParallel().WithDegreeOfParallelism(6)
let p = new Ping().Send(IPAddress.Parse(ipaddress))
select new
{
site,
Result = p.Status,
Time = p.RoundtripTime
}
/* process the information you got*/
首先,尝试该程序的单线程等效项。 您可能会发现它也使用了100%的核心,甚至可能更多(假设您有多个核心,显然超过100%/ 1是不可能的)。 示例代码是示例代码,并且在所有方面通常都不现实。
很多问题与100%的CPU利用率有关,因此可能会导致认为100%CPU ==坏东西。
实际上,100%的CPU ==昂贵的电子产品正在为你付钱做它的工作!
不幸的是,你付钱给它做的是遵循计算机程序中的指示。 如果计算机程序告诉它进入一个紧密的无限循环,那么它将花费尽可能接近100%的CPU(不同的调度程序在让其他线程做其他事情时比其他程序更好)。 这是100%CPU的经典坏情况。 是的,它正在做它被告知的事情,但它被告知是无意义的,永远不会走到尽头,遗憾的是它是如此“高效”,以至于它真的很好地保持其他线程不受影响。
让我们考虑另一个案例:
这里越接近100%越好。 每个%低于100表示CPU等待某事发生。 如果我们可以让“某些东西”更快地发生(可能更快的磁盘和内存),或者如果我们可以让CPU处理问题的另一部分,那么我们将更快地完成我们的完成点。 因此,如果我们用多线程方法替换代码,让它在另一个线程等待时利用CPU,并且如果这样做的开销没有抵消好处,那么我们就会获得性能提升。 (此外,这意味着我们可以使用x%的所有核心替换使用x%的一个核心的东西,并且因此更快)。
实际上,我们只需要做几次特定的工作,而不关心其他任何事情。 事实上,即使我们这样做,我们也会在此期间被UI所吓倒,忘记“让它看起来不被锁定而且永远不会回来”属于“其他任何事物”的范畴。
所以。 在现实世界中,我们做什么。
首先,我们检查一个真正的问题。 如果它暂时处于100%CPU,但是所有东西(包括其他进程)都可以完成它们的工作,那实际上很好 - CPU总是做一些事情,但这并不是因为一堆线程搞砸了它,它是因为所有要做的事情的线程都要去做。 快乐的时光。
然后我们检查我们实际上会遇到这种情况。 如果你有一个使用ax线程的多线程方法,每个方法都会花费大部分时间等待I / O,那么它们就不会遵循与你的例子相同的模式。 如果性能对于该特定任务至关重要,那么您实际上可能正在寻找重构它的方法,以便您可以在问题上抛出更多线程,因此CPU有更多时间做有用的事情,而当每个线程都在等待时更少一些东西。
如果我们发现流程中的CPU利用率损害了所有内容,那么我们可以做一些不同的事情:
只需使用一个线程。 除了所有其他过程的考虑因素之外,尽可能快地完成此过程是否真的很重要? 很多事情我们实际上并不想要这个。 真的是最重要的事情。
降低线程优先级。 我们考虑这只是一个完成答案的答案。 这样做有一些相当微妙的风险,最终会导致“优先级倒置”(简而言之,高优先级线程最终会等待低优先级线程,这意味着只有低优先级线程才能运行,而你在实践中得到与你想要的完全相反的优先级。
使用Yield
或Sleep
手动放弃CPU。 但是,如果你正在考虑这个问题,那么你必须问“这有什么不同,只是随意引入低效率?”。 如果你没有一个好的答案,那么单线程可能再次提供机器CPU的最佳总使用率而不是多线程。
是否需要运行所有的时间。 你说上面有关于监控的事情。 您真正需要的响应速度有多快? 如果用多线程方法检查你正在监视的所有内容需要0.01秒,并且你很高兴在它发生2秒后知道它,那么你的进程的效率是它需要的200倍,以牺牲其他过程为代价。 从计时器中解决问题。 (如果它需要一个单线程.5秒来完成所有这些,那么再次,为什么要多?)
以上所有内容仅考虑使用多线程来更快地完成特定任务的情况。 值得注意的是,这只是整个多线程模式范围的一个子集。 例如,如果您采用上面的计时器方法,但是使用单个线程然后执行工作,但是您在一个也在做其他事情的过程中这样做,那么这仍然算作多线程; 有一个线程正在执行该任务,而其他线程正在执行其他任务,并且希望整体响应能力良好。
多线程的整个想法是通过使用更多的计算资源(线程分布在核心之间)来更快地完成工作,从而提高CPU利用率。
如果要降低CPU利用率,请不要使用多线程,将程序粘贴到单线程。 它将运行更长时间,但消耗更少的CPU(当然,有很多优化可以减少CPU占用空间,但它们不是关于多线程)。
如果要监视网络中的300个节点,那就完全是另一回事了。 你的例子在这里是错误的,因为你尝试了计算密集型任务。 网络监控不是计算密集型的,它由“请求 - 等待 - 进程响应”循环组成,它们很好地并行化:即使一个CPU可以有效地处理来自一个节点的响应,同时等待来自另一个节点的响应。 更重要的是,因为网络等待实际上是一个I / O等待,所以这个等待可以很容易地卸载到您的操作系统,因此它不会消耗CPU。
在Richter的“CLR via C#,3rd edition”中有一个关于线程(和I / O等待)的好章节,我强烈建议您解决问题。
我读过你的处理器可以处理两种主要的不同类型的活动。 我可能在这些语句中有错误的措辞/语法,如果有人可以纠正语法,我将不胜感激。
计算工作(计算限制):向处理器提供纯计算任务,处理器可以在其上工作,无需外部设备或组件的任何输入。
基于输入的工作(I / O界限):当您的处理器正在处理某些事情时,还需要读取或写入某些内容,或者它需要等待网络活动。 例如,从磁盘读取文件或下载文件。
它们之间的主要区别在于计算应该在没有等待的情况下使用,以便在尽可能最快的时间内完成任务。 例如:
for(int i = 0; i<= 10000; i++)
{
}
没有与系统中任何“慢”部分的交互,所以对于这样的事情,你不介意计算/计算耗尽cpu,因为它可能无论如何都要在微秒的空间中完成。
这对于挖掘比特币或强制组合等特别重要。
你没有为这些添加'睡眠',因为它会不必要地减慢你的速度。
但是,如果您的工作负载是基于输入的,则需要读取或写入硬盘驱动器或网络; 与纯数学工作相比,被认为是缓慢的活动,然后添加Thread.Sleep(x)
并不是坏事,因为有时你的硬盘驱动器/ Ram速度产生数据的速度不如处理器所希望的那么快。
线程在这两个不同的主题中特别有趣,对于计算工作,您希望线程在100%不间断运行一段时间,那么您最好不要超过线程计数中的处理器计数。
例如: Environment.ProcessorCount
事实上,我几乎建议使用Environment.ProcessorCount -1
计数,即Environment.ProcessorCount -1
(在双核或更高核的情况下)使用100%的所有核心/处理器可能导致线程锁定,这实际上会阻碍性能。
我对此进行了实验,发现在双核系统上,我可以使用单核完成更多的循环/迭代,而不是完全使用两个核。
如果我使用四核,我发现通过使用3对全部4可以获得更多。
(别忘了,其中一个处理器必须共享操作系统功能,以及渲染窗体应用程序GUI - 如果有的话)
但是,如果您正在开发使用基于输入的计算的应用程序,它需要与许多慢速设备或依赖项进行交互,那么超出处理器数量可能并不是一件坏事。
例如:如果每个线程中都有很多thread.sleeps,那么您可以策略性地规划线程休眠,而其他线程可以工作。
我过去使用多线程实验室监视器完成了这项工作,该监视器旨在监视工作中实验室机器的状态; 对于每个实验室机器,线程都会运行。 但它实际上每10分钟才开始工作一次。
最初的问题,没有完全理解多线程的概念。
由于您有单线程并且它们没有延迟(线程)处理器被占用 100%。
即使您创建了多个任务(比如 100 个),那么所有任务都将并行执行并且处理器利用率将保持 100%
改变这个:
for (int i = 0; i < 10000000; i++) Console.Write("x");
进入这段代码:
for (int i = 0; i < 10000000; i++)
{
Console.Write("x");
Thread.Sleep(5);
}
采用
Thread.Sleep(x); //where x >= 0
要么
Thread.Yield();
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.