我正在尝试以低优先级运行进程。但我并没有在ProcessPriorityClass中将其设置为低的选项。但如果我转到任务管理器,我可以手动将流程优先级设置为低。那么怎样才能做到呢?在下面的代码中,我曾将进程设置为低于正常优先级。
Dim s As New Process
s.StartInfo.FileName = "D:myapp.exe"
s.Start()
s.PriorityClass = ProcessPriorityClass.BelowNormal
没有"Low"值,尽管由于某种原因,默认任务管理器会将ProcessPriorityClass.Idle
称为"Low"(大多数其他任务管理器,如ProcessHacker或ProcessExplorer,称其为"Idle",如果你在处理进程,你可能应该得到一个像ProcessHacker这样的不错的任务管理器替代品,而不是针对技术含量较低的用户的内置应用程序)。
将进程设置为ProcessPriorityClass.Idle
对我来说很好。
不过需要注意的是:更改流程的优先级级别通常是个坏主意,尤其是在空闲和流程之外(至少在流程内部,您可以决定将某些任务的优先级更改得更高,然后再次设置)。
如果进程以不可共享的方式获得任何资源,设置如此低的优先级可能会导致严重的死锁,因为当任何更高优先级的进程需要一个时间片时,它都不会运行(尤其是在内核很少的机器上,因为同时可用的时间片更少),所以如果更高优先级进程也需要资源,它将永远等待获得,因为优先级较低的进程将没有机会运行。
最终,Windows(虽然不是某些早期版本)将通过临时将优先级较低的进程中等待运行的所有线程提升到高于ProcessPriorityClass.High
进程中的ThreadPriority.Highest
线程,然后逐渐降低到较低的优先级来解决此问题,此时问题再次发生。
这可能与你希望发生的事情相反。
因为这在内核很少的机器上尤其残酷,如果你的开发设备很强大,你可能会遇到这样的情况,即你的机器上一切都很好,然后使用不那么强大的机器的用户会发现一切都会停止。
默认情况下,只有一些中断和"系统空闲进程"(这是一种特殊情况)在空闲状态下运行,这是有充分理由的。
尽管如此,如果你确信自己知道自己在做什么(或者说,如果你在做实验),那么s.PriorityClass = ProcessPriorityClass.Idle
就是你想要的。
编辑:更多关于优先级的信息:
给定线程相对于进程中的其他线程具有优先级,而给定进程相对于系统上的其他进程具有优先级。
一个线程相对于系统上所有其他线程的优先级取决于这两个线程,如下表所示:
Thread: | Idle | Lowest | Below | Normal | Above | Highest | Time-Critical
| | | Normal | | Normal | |
---------------+------+--------+--------+--------+--------+---------+--------------
Idle Process | 1 | 2 | 3 | 4 | 5 | 6 | 15
Below-Normal | 1 | 4 | 5 | 6 | 7 | 8 | 15
Normal Process | 1 | 6 | 7 | 8 | 9 | 10 | 15
Above-Normal | 1 | 8 | 9 | 10 | 11 | 12 | 15
High Process | 1 | 11 | 12 | 13 | 14 | 15 | 15
Realtime | 16 | 22 | 23 | 24 | 25 | 26 | 31
现在,任何一台给定的计算机都将有X核,其中当前的常见值为1、2、4、8或16。每个内核一次只能运行一个线程。
如果想要运行的线程比内核多,那么调度如下:
- 从现有的最高优先级线程开始。在它们之间共享核心
- 如果线程多于核心,请在它们之间循环使用核心,这样具有该优先级的所有线程都可以获得相同的共享
- 如果有剩余的核心,则对下一个优先级最高的线程执行同样的操作,依此类推
所以,如果有4个核心,我们有3个线程优先级8(例如,正常进程中的正常线程),2个优先级10(例如,在正常进程中高于正常线程)和3个优先级6(空闲进程中的最高优先级线程),它们都准备好运行:
- 两个优先级为10的线程总是在一个核心上获得一个时间片
- 剩下的2个核心将在3个正常/正常线程之间进行时间共享
- 3个优先级为6的线程将没有机会运行
这对这3个线程来说是不好的,但它对整个系统来说应该是好的,因为那3个线程应该只用于重要性很低的事情,我们完全可以不运行它们。
这些数字还通过以下方式得到提升:
- 启用前台提升(对于桌面正常,对于服务器不正常)如果一个正常进程拥有前台窗口,它将被提升到不拥有前台窗口的正常进程之上
- 当一个窗口接收到鼠标、键盘或计时器输入,或来自另一个窗口的消息时,其进程会得到暂时的提升
- 如果一个线程正在等待某个东西,而该东西已经准备好了,它就会得到提升
- 如果一个线程已经准备了很长时间而没有运行,它可能会随机获得很大的提升
前三个应该是合理的常识,因为很明显,人们可能希望增强这些进程或线程。
第四个是引入一个温和的问题来解决一个严重的问题:如果线程a需要线程B拥有的低优先级资源,并且只有一个核心可用,那么它们将死锁,因为线程B不会获得时间片,所以它不会释放资源,所以线程a会继续尝试获得资源,所以不会结束,所以线程B不会得到时间片。。。
因此操作系统将线程B提升为临时超高优先级,而线程A一点也没有得到关注,从这一点到死锁的最初减缓,整个系统的速度比它应该的慢得多(拥有多核系统的一个实际优势并不是让很多繁忙的进程更好地协同工作,而是让这种情况发生的可能性大大降低)。
所有这些的结果是,99%的情况下,线程的最佳优先级是正常的,进程的最佳优先级也是正常的。
Idle/Low应该为那些微不足道的过程保留下来,我们真的不在乎它们是否永远没有机会做某事。屏幕保护程序确实是一个例子,因为如果屏幕保护程序从来没有机会运行,它可能就不应该运行;没有人为了观看飞行的烤面包机而在最先进的钻机上花很多钱(尽管在20世纪90年代,人们可能会想知道)。
做得好的非正常优先级示例:
"不要那样做"建议的问题在于,它总是不完整的;在很多事情上,"不要那样做"通常是最好的建议(例如,扰乱GC是另一个例子),但如果不了解例外情况,人们就无法真正理解为什么"不要那样"是好建议,事实上,除非你涵盖例外情况,否则这不是真正的好建议,这只是教条。因此,值得考虑的是高优先级和低优先级的好案例,以及它们如何消除所涉及的问题。
实时处理现场媒体
如果你正在处理现场音乐或视频,并且需要专业的输出(你实际上是在为他人录制或传输这些内容,而不仅仅是在自己的机器上观看),那么在以最高优先级运行的线程中这样做是有意义的——也许进程设置为实时。这会对整个系统造成惩罚,但现在这台机器上最重要的是进行媒体处理的过程,考虑到媒体流出现故障的选择,以及对系统造成严重问题的情况,你宁愿系统出现严重问题,然后再处理。所有"与其他进程玩得很好"的正常概念都是不可能的,事实上,在*nix系统上,出于这种目的,这类工作通常是用实时内核来完成的,该内核针对可预测的时间进行了优化,但代价是整体并发性能不太好(尽管上面的所有其他细节也会有所不同,但我只在上面解释了Windows)。
.NET中的Finalizer线程
.NET中的finalizer线程以高优先级运行。大多数时候,这个线程没有任何事情可做,所以它不是活动的。当它确实有事情要做时(最终确定队列不是空的),那么无论有多少其他线程在运行,它都要这样做是至关重要的。一些重要注意事项:
- 任何写得好的最终确定程序都应该执行得很快,所以处理所有最终确定程序所花费的总时间应该很短。(最终确定,如果需要很长时间,在某些情况下甚至可能被放弃)
- 任何写得好的定稿器都不应该干扰其他线程,因此处理所有定稿器都应该不干扰其他线程
这两个事实对于最大限度地减少优先级高于正常线程的负面影响很重要,因为它们意味着它不会遇到优先级反转问题,而且大多数时间都没有运行,因此不会与其他线程竞争。
系统空闲过程
让一个特殊的进程在空闲状态下运行有两个目的。首先,由于总是有一个进程在空闲状态下运行,每个核心都有一个线程,因此调度程序不需要任何代码来处理没有线程可运行的情况,因为总是有这样的线程,如果没有其他线程,则上述逻辑将运行其中一个线程。
第二,这些线程可以调用内核所具有的任何节能或低锁定能力,因为如果它们运行任何时间长度,那么根据定义,CPU都不需要,应该处于低功耗状态。
重要的是,这个过程永远不会获得另一个过程可能想要的任何不可共享的资源,因此它永远不会导致优先级反转问题。
在这里,我们可以看到,这个过程的目的意味着,如果有任何其他线程要做,我们几乎永远不希望它运行
(它还为我们提供了一个很好的衡量标准,来衡量什么时候某个东西应该处于低优先级;如果它不应该与一个只为使CPU进入低功耗状态而存在的线程竞争,那么它就不应该是低优先级)。
我猜ProcessPriorityClass.Idle
在任务管理器中的意思是"低"。
另请参阅:ProcessPriorityClass枚举