【问题标题】:Threading example线程示例
【发布时间】:2011-12-19 12:24:01
【问题描述】:

我在互联网上找到了这本书:Threading in C# Joseph Albahari。我试过它的例子:

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在这里是什么样子的?还有,你有多少个内核?
  • 但是先生,我想有效地监控网络中的 300 个节点,我认为多线程是解决方案,但它会将 cpu 提高到如此高的利用率。我还应该使用什么来有效地监控 300 个节点而不会对 cpu 造成太大的负载?
  • @user1068846:那是完全不同的故事。请发布一个描述该问题的不同问题。这与您在这里向我们展示的内容无关。
  • @Joe - 我的评论稍微简化了一点。但是,当您在线程上启动 CPU 密集型任务时,目的在多核上更高的 CPU 利用率。

标签: c# multithreading


【解决方案1】:

如果您可以同时使用多个资源,多线程可以改进您的应用程序。 例如,如果您有多个内核或多个 CPU,我相信上面的示例应该会表现得更好。

或者,如果您有一个使用 CPU 的线程,以及另一个同时使用磁盘的线程,那么如果您使用多线程,它的性能也会更好。

但是,如果您只有一个 CPU 或一个内核,则上述示例的性能不会更好。效果会更差。

【讨论】:

  • 只有您的回答和 Tudor 的回答似乎都明白发生了什么。这确实是作者问题的唯一答案。
【解决方案2】:

您无法降低利用率,因为您正在使用两个线程(很可能在双核上),这两个线程都是工作密集型的(它们循环并打印一些东西)。也许降低线程优先级会有所帮助,但我认为这不是这个例子的重点。

【讨论】:

    【解决方案3】:

    WriteY 函数中的循环将尽快执行。因此它将使用 100% 的 CPU。如果您希望它减少资源密集型,您可以做两件事:

    • 更改线程的优先级。这样,您的应用仍将使用 100% 的 CPU,但如果另一个线程需要 CPU 资源,该线程将“减速”

    • 在您的 WriteY 函数中添加暂停:

      static void WriteY()
      {
          for (int i = 0; i < 10000000; i++)
          { 
              Console.Write("y");
              Thread.Sleep(100);
          }
      }
      

    【讨论】:

    • 更改优先级是危险的。我自己提到过,但 IMO,值得指出的是,如果这样做可能会导致更大的问题。
    【解决方案4】:

    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.     
             } 
        } 
    }
    

    更新

    好吧,如果你有多个核心,你可以使用This example,使用 .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*/
    

    【讨论】:

    • 你还需要一些花括号。仅添加该行不会改善情况。 - 只是因为他看起来像个初学者,可能会弄错
    • WithDegreeOfParallelism(6) 是什么意思?
    • MSDN 声明“并行度是用于处理查询的并发执行任务的最大数量。”
    【解决方案5】:

    首先,尝试该程序的单线程等效程序。您可能会发现它也使用了 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 总是在做一些事情,但这并不是因为一堆线程把它搞砸了,而是因为所有有事情要做的线程都在做。快乐的日子。

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

    如果我们确实发现进程的 CPU 利用率正在损害一切,那么我们可以做一些不同的事情:

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

    2. 降低线程优先级。不过,让我们认为这是一个仅用于完成的答案。这样做有一些非常微妙的风险,最终可能导致“优先级反转”(简而言之,高优先级线程最终会等待低优先级线程,这意味着只有低优先级线程才能运行,而你在实践中获得与您想要的完全相反的相对优先级)。

    3. 使用YieldSleep 手动放弃CPU。但是,如果您正在考虑这一点,您必须问“这与任意引入低效率有何不同?”。如果您没有一个好的答案,那么单线程可能比多线程更能充分利用您机器的 CPU。

    4. 它是否需要一直运行。你上面说了一些关于监控的事情。您真正需要多快的响应速度?如果使用多线程方法检查您正在监视的所有事情需要 0.01 秒,并且您很乐意在它发生 2 秒后知道它,那么您的流程的效率是需要的 200 倍,以牺牲其他进程为代价。而是从计时器开始。 (如果它需要一个单线程 0.5 秒来完成这一切,那么为什么要多线程呢?)

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

    【讨论】:

      【解决方案6】:

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

      如果您想降低 CPU 使用率,请不要使用多线程,将您的程序坚持使用单线程。它将运行更长时间,但消耗更少的 CPU(当然,有很多优化可以减少 CPU 占用,但它们与多线程无关)。

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

      Richter 的“CLR via C#,第 3 版”中有一个关于线程(和 I/O 等待)的好章节,我强烈推荐它来解决您的问题。

      【讨论】:

        【解决方案7】:

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

        1. 计算工作(计算绑定):将纯计算任务交给处理器,处理器可以处理它,而无需来自外部设备或组件的任何输入。

        2. 基于输入的工作(I/O 绑定):当您的处理器正在处理某事,但还需要在磁盘上读取或写入某些内容,或者它需要等待网络活动时。例如从磁盘读取文件或下载文件。

        它们之间的主要区别在于,计算应该在没有等待的情况下使用,以便在尽可能快的时间内完成任务。 例如:

        for(int i = 0; i<= 10000; i++)
        {
        
        }
        

        与系统的任何“慢”部分都没有交互,所以对于这样的事情,你不介意计算/计算耗尽 cpu,因为它很可能在微秒的空间内完成。

        这对于诸如比特币挖矿或暴力破解组合之类的事情尤其重要。

        您不要在其中添加“睡眠”,因为它会不必要地减慢您的速度。

        但是,如果您的工作负载是基于输入的,则需要读取或写入您的硬盘或网络;与纯数学工作相比被认为很慢的活动,那么添加Thread.Sleep(x) 并不是一件坏事,因为有时您的硬盘驱动器/ RAM 产生数据的速度不如您的处理器想要的那么快。

        线程在这两个不同的主题中特别有趣, 对于计算工作,您希望线程在一段时间内以 100% 不间断运行,那么线程数最好不要超过处理器数。

        例如:Environment.ProcessorCount

        事实上,我几乎建议使用 Environment.ProcessorCount -1 的线程数(在双核或更高的情况下) 使用 100% 的所有内核/处理器可能会导致线程锁定,这实际上会影响性能。

        我对此进行了试验,发现在双核系统上,与完全使用两个核相比,我可以使用单核执行更多循环/迭代。

        如果我使用四核,我发现使用 3 与完全使用 4 可以获得更多。

        (别忘了,其中一个处理器必须共享操作系统功能,以及呈现 Windows 窗体应用程序 GUI - 如果有的话)

        但是,如果您正在开发一个使用基于输入的计算的应用程序,它需要与许多慢速设备或依赖项进行交互,那么超出您的处理器数量可能不是一件坏事。

        例如:如果每个线程中都有很多 thread.sleep,那么您可以策略性地计划您的线程休眠,而其他线程工作。

        我过去曾使用多线程实验室监视器完成此操作,该监视器旨在监视工作中的实验室机器的状态;对于每台实验室机器,都会运行一个线程。但它实际上每 10 分钟才工作一次。

        【讨论】:

          【解决方案8】:

          改变这个:

          for (int i = 0; i < 10000000; i++) Console.Write("x");
          

          进入这段代码:

          for (int i = 0; i < 10000000; i++) 
              {
                 Console.Write("x");
                 Thread.Sleep(5);
              }
          

          【讨论】:

            【解决方案9】:

            使用

            Thread.Sleep(x); //where x >= 0
            

            Thread.Yield();
            

            【讨论】:

              【解决方案10】:

              最初的问题,没有完全理解多线程的概念。

              由于您有单线程并且它们没有延迟(线程)处理器被占用 100%。

              即使您创建多个任务(例如 100 个),所有任务也将并行执行,并且处理器利用率将保持 100%

              【讨论】:

              猜你喜欢
              • 2020-09-20
              • 1970-01-01
              • 2011-03-11
              • 2016-11-17
              • 2017-11-05
              • 1970-01-01
              • 2016-12-21
              • 2011-02-01
              • 1970-01-01
              相关资源
              最近更新 更多