优先级最低的线程被调用的次数更多
本文关键字:调用 线程 优先级 | 更新日期: 2023-09-27 18:28:45
下面的代码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
为什么有区别?
Priority
支持未在Mono中实现,因此您看到的关于Lowest的行为被多次调用可能只是运气。
有一个来自github中实现该属性的贡献者的拉取请求,所以你可能想和他一起要求对其进行审查。
更新:这个答案可能已经过时了。随着Mono的快速发展,您最好重新测试您的程序,也许Priority现在可以工作。
发生这种情况的原因有很多。
有一种叫做"优先级提升"的东西,也是其中之一,操作系统会出于各种原因提高优先级(暂时提高线程的优先级)例如:一个线程刚刚收到一个事件信号,是UI线程为消息泵送,并且在其队列中收到了一条新消息,另一个原因可能是饥饿。
饥饿可能就是这种情况。当有一个优先级较高的线程准备就绪或正在运行时,低优先级线程将没有机会执行。如果线程长时间处于饥饿状态,操作系统将提高处于饥饿状态的线程的优先级,以便它立即抢占正在运行的线程,并获得执行的机会,从而取消饥饿时间。
我在windows中进行了测试,结果非常一致,下面是解释。操作系统出于怜悯之心给饥饿的线程提供了动力。我想类似的东西也存在于其他操作系统中。
Windows有一项名为"资产负债表管理器"的服务。它在运行在系统线程上异步地寻找饥饿线程;这些是等待在就绪状态下运行4个的线程秒或更长时间。如果它找到一个,它会给线程一个临时优先级提升。它总是提高缺少线程的优先级至级别15,而不管其当前值如何。这样做是为了战斗例如,当许多优先级较高的线程不断运行,使得优先级较低的线程永远不会有机会执行。
引用《Windows并发编程》一书Joe Duffy
更新:
至于您在2核windows操作系统中的更新结果,是的,结果并不奇怪,没有足够的cpu来运行处于就绪状态的线程,因此操作系统必须等待其他线程完成线程量子。有足够多的优先级较高的线程比优先级较低的线程,所以很明显操作系统会优先考虑优先级较高的螺纹。
对于8核的结果:如前所述,mono不支持优先级,您首先启动了优先级较低的线程,并且您有足够的处理器来运行可运行的线程。因此,这里没有必要抢占优先级较低的线程。所以操作系统允许线程在不干扰(抢占)的情况下运行。尝试使用比处理器数量更多的线程进行测试。这才是真正需要优先考虑的地方。
您首先启动优先级最低的线程,然后升级到优先级最高的线程。这使优先级较低的线程有时间运行,而优先级较高的线程启动(启动线程不是一个简单的操作)。
创建另一个布尔标志来调节线程函数中的计数,以便在任何线程开始计数之前,所有线程都已启动:
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);
首先,不要干扰线程优先级。或处理优先级。你不会有任何帮助,而且在进行过程中很可能会导致许多死锁和其他同步问题,这将是非常有趣的调试。相信操作系统的线程调度程序可以完成它的工作——它实际上非常擅长!
其次,Mono不支持优先级。
第三,您可能希望给线程足够的工作,以便给它们一个切换的理由。八核CPU上的五个线程根本不会得到足够的争用来保证任何线程切换,而且它们可能都以相同的速度运行。要尝试线程优先级,需要比CPU内核更多的线程来完成工作。是的,超线程核心也很重要。
不要只做一个"愚蠢"的计数器——在每个计数器步骤中为CPU添加一些愚蠢的工作,比如Thread.SpinWait(100000)
。这会给CPU带来一些愚蠢的压力,这会让你的计数更有趣,更具可比性。
我从未见过更改线程优先级的好例子。总有更好的解决方案,你很可能试图解决一个不存在的问题,或者可以用更好的方式解决。Jeff Atwood的http://blog.codinghorror.com/thread-priorities-are-evil/.
请注意,像Task
这样的高级多线程/并行结构没有任何方式来建议优先级或类似的东西——这是有充分理由的。
小心:)