仅仅因为一个线程具有较高的优先级,并不意味着其他低优先级的线程就不会得到机会运行。
这怎么理解?
有时候,我看到人们编写多线程代码,将一个线程的优先级置于另一个线程之上,他们认为这将防止优先级较低的线程干扰较高优先级线程的操作,这样他们就可以不用编写线程同步的代码了,如下图所示:
>> 请移步至 topomel.com 以查看图片 <<
我们先不考虑缓存一致性的情景。如果保证低优先级线程永远不会在高优先级线程运行时运行,则此代码看起来没有问题。即使高优先级线程在低优先级线程检查 ready 标志后中断并设置结果,发生的情况也只是低优先级线程错过了结果。(这不是一个新问题,因为同时性的相对性原理表明无论如何这都是可能的。)
但是,不能保证低优先级线程不会干扰高优先级线程。
系统调度程序的规则是,查找具有最高优先级的”可运行”线程,即准备运行,并将其分配给 CPU 执行。为了准备好运行,线程不能在任何情况上被阻塞,也不能已经在另一个 CPU 上运行。如果可运行的线程之间存在最高优先级的平局,则调度程序在它们之间大致相等地共享 CPU。
你可能会认为,给定这些规则,只要存在可运行的高优先级线程,就不会运行低优先级线程。但事实并非如此。
考虑多处理器系统的情况(随着超线程的出现,这变得越来越普遍),其中有两个可运行的线程,一个的优先级高于另一个。调度程序将首先将高优先级线程分配给其中一个处理器。但它仍然有一个备用 CPU 可用,因此低优先级线程将分配给第二个 CPU。现在,你有一个优先级较低的线程作为优先级较高的线程同时运行。
当然,即使系统中存在优先级较高的线程,较低优先级线程也可以运行的另一种方式是,所有优先级较高的线程都被阻塞。除了你可能期望的情况(即等待同步对象(如信号量或临界区))之外,线程还可以被 I/O 阻塞或者被页换出。页换出在这里是一个泛化的概念,因为你无法控制系统何时可能由于系统中其他位置的内存压力而决定将你正在使用的内存换出。
这个故事的寓意是,线程优先级不能替代适当的线程同步。
总结
目前为止,在拓扑梅尔智慧办公平台(Topomel Box)的开发中,我从未考虑特意将某个线程置于高优先级。
碰到需要跨线程共享内存的情况,我一般都是老老实实地使用线程同步机制来做。
小心驶得万年船!
最后
Raymond Chen的《The Old New Thing》是我非常喜欢的博客之一,里面有很多关于Windows的小知识,对于广大Windows平台开发者来说,确实十分有帮助。
本文来自:《Consequences of the scheduling algorithm: Low priority threads can run even when higher priority threads are running》