优先级最低的线程被调用多次

时间:2014-10-31 07:52:34

标签: c# multithreading mono task-parallel-library thread-priority

以下代码5个具有不同优先级的线程正在竞争访问具有8个内核的CPU(Mac OS X 10.8.5,Mono)。每个线程增加其计数器。

using System;
using System.Threading;

      class PriorityTesting 
     { 
       static long[] counts; 
       static bool finish;

       static void ThreadFunc(object iThread) 
       { 
         while(true) 
         { 
           if(finish) 
              break; 
           counts[(int)iThread]++; 
         } 
       }

       static void Main() 
       { 
         counts = new long[5]; 
         Thread[] t = new Thread[5]; 
         for(int i=0; i<t.Length; i++)  
         { 
           t[i] = new Thread(ThreadFunc); 
           t[i].Priority = (ThreadPriority)i; 
         } 
         // Запускаем потоки 
         for(int i=0; i<t.Length; i++) 
           t[i].Start(i);

         // Даём потокам возможность поработать 10 c 
         Thread.Sleep(10000);

         // Сигнал о завершении 
         finish = true;

         // Ожидаем завершения всех потоков 
         for(int i=0; i<t.Length; i++) 
           t[i].Join(); 
         // Вывод результатов 
         for(int i=0; i<t.Length; i++) 
           Console.WriteLine("Thread with priority {0, 15}, Counts: {1}", (ThreadPriority)i, counts[i]); 
       }   
     }

汇编:

$ mcs PriorityTesting.cs
$ mono PriorityTesting.exe

输出:

Thread with priority          Lowest, Counts: 178544880
Thread with priority     BelowNormal, Counts: 167783608
Thread with priority          Normal, Counts: 160593225
Thread with priority     AboveNormal, Counts: 79123315
Thread with priority         Highest, Counts: 81623159

具有最低优先级的线程如何比具有最高优先级的线程调用多次?

UPD:

具有2个内核的CPU上的相同代码(Windows,.NET):

 Thread with priority         Lowest, Counts:    7608195 
 Thread with priority    BelowNormal, Counts:   10457706 
 Thread with priority         Normal, Counts:   17852629 
 Thread with priority    AboveNormal, Counts:  297729812 
 Thread with priority        Highest, Counts:  302506232

为什么会有差异?

4 个答案:

答案 0 :(得分:6)

{1}}支持未在Mono中实现,因此您所看到的有关最低调用次数的行为可能仅仅是运气。

在github中有来自贡献者的拉取请求,因此你可能想和他一起要求对它进行审核。

更新:此答案可能已过时。随着Mono的迅速发展,你重新测试你的程序会更好,也许现在优先工作。

答案 1 :(得分:3)

这可能由于很多原因而发生。

有一种称为“优先级提升”的东西,其中之一就是其中之一,操作系统会因各种原因提升优先级(临时增加线程的优先级)示例:一个线程刚收到一个事件信号,它就是UI为消息泵送的线程,并且在其队列中收到新消息,另一个原因可能是饥饿

饥饿可能就是这里的情况。当优先级较高的线程已准备就绪或当前正在运行时,您的低优先级线程将无法执行。如果一个线程长时间处于饥饿状态,操作系统将提升正在饥饿的线程的优先级,以便它立即抢占正在运行的线程并有机会执行,从而取消饥饿时间。

我在Windows中测试过,结果非常相似,这是解释。操作系统使得饥饿的线程更加怜悯。我想其他操作系统也存在这样的事情。

  

Windows有一项称为余额集管理器的服务。它运行   在寻找饥饿线程的系统线程上异步;这些   是等待在4的就绪状态下运行的线程   秒或更长时间。如果找到一个,它将给线程a   暂时优先提升。它总是会增加一个饥饿线程的优先级   等级为1 5,无论其当前值如何。这是为了战斗   例如,当许多优先级较高的线程出现时,就会出现饥饿现象   经常运行,以便优先级较低的线程永远不会有机会   执行。

从书籍Concurrent Programming on Windows-Joe Duffy

中引用

<强>更新

至于在具有2个内核的Windows操作系统中的更新结果,是的结果并不奇怪没有足够的CPU来运行处于就绪状态的线程,因此操作系统必须等到其他线程完成其线程量子。有足够高优先级的线程而不是更低的优先级,因此显然操作系统优先考虑更高优先级的线程。

对于8核的结果:如上所述mono不支持优先级,您首先启动了优先级较低的线程,并且您有足够的处理器来运行可运行的线程。因此,无需在此处抢占较低优先级的线程。因此操作系统允许线程在不干扰(抢占)的情况下运行。尝试使用比您拥有的处理器数量更多的线程进行测试。这就是优先考虑的地方。

答案 2 :(得分:1)

您首先启动优先级最低的线程,升级到最高优先级。这样可以在优先级较高的线程启动时运行较低优先级的线程(启动线程不是一个简单的操作)。

创建另一个布尔标志来调节线程函数中的计数,以便在任何线程开始计数之前所有线程都已启动:

   static long[] counts; 
   static bool finish;
   static bool count; // add this

   static void ThreadFunc(object iThread) 
   { 
     while(true) 
     { 
       if(finish) 
          break;

       if( count ) // only count when ready
           counts[(int)iThread]++; 
     } 
   }

Main

   // After your loop to start the threads
   // set the count flag to start counting
   count = true;
   // Даём потокам возможность поработать 10 c 
   Thread.Sleep(10000);

答案 3 :(得分:1)

首先 - 不要乱用线程优先级。或处理优先级。您无法提供任何帮助,并且您很可能会导致许多死锁和其他同步问题,这将是很多有趣的调试。相信操作系统&#39;线程调度程序完成它的工作 - 它实际上相当不错!

其次,Mono不支持优先事项。

第三,您可能希望为线程提供足够的工作来实际给他们切换的理由。八核CPU上的五个线程根本没有获得足够的争用来保证任何线程切换,并且它们可能都以相同的速度运行。要试验线程优先级,您需要更多的线程在工作,而不是CPU核心。是的,Hyper-Threaded核心也很重要。

不要做一个&#34;愚蠢的&#34; counter - 在每个计数器步骤中为CPU添加一些愚蠢的工作,如Thread.SpinWait(100000)。这会给CPU带来一些愚蠢的压力,这将使你的计数更有趣和可比。

我从未见过改变线程优先级的好例子。总是有一个更好的解决方案,而且您最有可能尝试解决一个问题,或者不是那个问题,或者可以用更好的方式解决。关于优先事项问题的一个好的入门参考是Jeff Atwood的http://blog.codinghorror.com/thread-priorities-are-evil/

请注意,像Task这样的高级多线程/并行结构没有任何方式可以建议优先级或类似的东西 - 这是一个很好的理由。

小心:)