以下代码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
为什么会有差异?
答案 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
这样的高级多线程/并行结构没有任何方式可以建议优先级或类似的东西 - 这是一个很好的理由。
小心:)