繁体   English   中英

线程示例 C#

[英]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的经典坏情况。 是的,它正在做它被告知的事情,但它被告知是无意义的,永远不会走到尽头,遗憾的是它是如此“高效”,以至于它真的很好地保持其他线程不受影响。

让我们考虑另一个案例:

  1. 将要完成的工作量是有限的 - 在某些时候它已经完成了。
  2. 你没有其他任何你想要的计算机。

这里越接近100%越好。 每个%低于100表示​​CPU等待某事发生。 如果我们可以让“某些东西”更快地发生(可能更快的磁盘和内存),或者如果我们可以让CPU处理问题的另一部分,那么我们将更快地完成我们的完成点。 因此,如果我们用多线程方法替换代码,让它在另一个线程等待时利用CPU,并且如果这样做的开销没有抵消好处,那么我们就会获得性能提升。 (此外,这意味着我们可以使用x%的所有核心替换使用x%的一个核心的东西,并且因此更快)。

实际上,我们只需要做几次特定的工作,而不关心其他任何事情。 事实上,即使我们这样做,我们也会在此期间被UI所吓倒,忘记“让它看起来不被锁定而且永远不会回来”属于“其他任何事物”的范畴。

所以。 在现实世界中,我们做什么。

首先,我们检查一个真正的问题。 如果它暂时处于100%CPU,但是所有东西(包括其他进程)都可以完成它们的工作,那实际上很好 - CPU总是做一些事情,但这并不是因为一堆线程搞砸了它,它是因为所有要做的事情的线程都要去做。 快乐的时光。

然后我们检查我们实际上会遇到这种情况。 如果你有一个使用ax线程的多线程方法,每个方法都会花费大部分时间等待I / O,那么它们就不会遵循与你的例子相同的模式。 如果性能对于该特定任务至关重要,那么您实际上可能正在寻找重构它的方法,以便您可以在问题上抛出更多线程,因此CPU有更多时间做有用的事情,而当每个线程都在等待时更少一些东西。

如果我们发现流程中的CPU利用率损害了所有内容,那么我们可以做一些不同的事情:

  1. 只需使用一个线程。 除了所有其他过程的考虑因素之外,尽可能快地完成此过程是否真的很重要? 很多事情我们实际上并不想要这个。 真的是最重要的事情。

  2. 降低线程优先级。 我们考虑这只是一个完成答案的答案。 这样做有一些相当微妙的风险,最终会导致“优先级倒置”(简而言之,高优先级线程最终会等待低优先级线程,这意味着只有低优先级线程才能运行,而你在实践中得到与你想要的完全相反的优先级。

  3. 使用YieldSleep手动放弃CPU。 但是,如果你正在考虑这个问题,那么你必须问“这有什么不同,只是随意引入低效率?”。 如果你没有一个好的答案,那么单线程可能再次提供机器CPU的最佳总使用率而不是多线程。

  4. 是否需要运行所有的时间。 你说上面有关于监控的事情。 您真正需要的响应速度有多快? 如果用多线程方法检查你正在监视的所有内容需要0.01秒,并且你很高兴在它发生2秒后知道它,那么你的进程的效率是它需要的200倍,以牺牲其他过程为代价。 从计时器中解决问题。 (如果它需要一个单线程.5秒来完成所有这些,那么再次,为什么要多?)

以上所有内容仅考虑使用多线程来更快地完成特定任务的情况。 值得注意的是,这只是整个多线程模式范围的一个子集。 例如,如果您采用上面的计时器方法,但是使用单个线程然后执行工作,但是您在一个也在做其他事情的过程中这样做,那么这仍然算作多线程; 有一个线程正在执行该任务,而其他线程正在执行其他任务,并且希望整体响应能力良好。

多线程的整个想法是通过使用更多的计算资源(线程分布在核心之间)来更快地完成工作,从而提高CPU利用率。

如果要降低CPU利用率,请不要使用多线程,将程序粘贴到单线程。 它将运行更长时间,但消耗更少的CPU(当然,有很多优化可以减少CPU占用空间,但它们不是关于多线程)。

如果要监视网络中的300个节点,那就完全是另一回事了。 你的例子在这里是错误的,因为你尝试了计算密集型任务。 网络监控不是计算密集型的,它由“请求 - 等待 - 进程响应”循环组成,它们很好地并行化:即使一个CPU可以有效地处理来自一个节点的响应,同时等待来自另一个节点的响应。 更重要的是,因为网络等待实际上是一个I / O等待,所以这个等待可以很容易地卸载到您的操作系统,因此它不会消耗CPU。

在Richter的“CLR via C#,3rd edition”中有一个关于线程(和I / O等待)的好章节,我强烈建议您解决问题。

我读过你的处理器可以处理两种主要的不同类型的活动。 我可能在这些语句中有错误的措辞/语法,如果有人可以纠正语法,我将不胜感激。

  1. 计算工作(计算限制):向处理器提供纯计算任务,处理器可以在其上工作,无需外部设备或组件的任何输入。

  2. 基于输入的工作(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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM