第5章 不要让线程成为脱缰的野马(Keeping your Threads on Leash) ---线程优先权(Thread priority)
有没有过这样的经验?你坐在你的车子里,目的地还在好几公里之遥,而时间已经很晚了。你拼命想告诉那些挡住你去路的人们,今天这个约会对你是多么多么重要,能不能请他们统统……呃……滚到马路外?很不幸,道路系统并没有纳入所谓的优先权观念。如果有某条专用道是给“非常重要”的通行所用的,你就可以摆脱那些如潮水般在你四周的车辆和行人,岂不甚妙?
Win32 有所谓的优先权(priority)观念,用以决定下一个获得 CPU 时间的线程是谁。较高优先权的线程必然获得较多的 CPU 时间。关于优先权的完整讨论其实相当复杂。你可以无分轩轾地给予每一个线程相同的优先权,这可能会使你承担不少麻烦。你也可以明智地使用优先权,使自己能够调整程序的执行次序。例如你可以设定你的 GUI 线程有较高优先权,使它对于用户的反应能够比较平顺一些,或者你可以改变 worker 线程的优先权,使它们只在系统的闲置时间(idle time)里工作。
Win32 优先权是以数值表现的,并以进程的“优先权类别(priority class)”、线程的“优先权层级 (priority level)”和操作系统当时采用的“动态提升(Dynamic Boost)”作为计算基准。所有因素放在一起,最后获得一个 0~31 的数值。拥有最高优先权之线程,即为下一个将执行起来的线程。如果你有一大把 worker 线程,其“优先权类别”和“优先权层级”都相同,那么就每一个轮流执行。这是所谓的 “round robin” 调度方式。如果你有一个线程总是拥有最高优先权,那么它就永远获得 CPU 时间,别人都别玩了。这就是为什么必须明智而谨慎地使用优先权的原因。
优先权类别(Priority Class)
“优先权类别”是进程的属性之一。这个属性可以表现出这一进程和其他进程比较之下的重要性。Win32 提供四种优先权类别,每一个类别对应一个基本的优先权层级。表格5-1 展示了四个优先权类别。
表格5-1 优先权类别(Priority Classes)
优先权类别(Priority Classes) 基础优先权值(base priority)
HIGH_PRIORITY_CLASS 13
IDLE_PRIORITY_CLASS 4
NORMAL_PRIORITY_CLASS 7 or 8(译注:有些资料上写 7 or 9)
REALTIME_PRIORITY_CLASS 24
大部分程序使用 NORMAL_PRIORITY_CLASS。少数情况下才会考虑使用其他类别。例如,Task Manager 就是使用 HIGH_PRIORITY_CLASS,所以即使其他程序处于非常忙碌的状态下,它也总是能够有所反应。
Windows NT 中有一个好例子,可以说明到底应不应该使用某些特定的优先权类别。以 OpenGL 完成的屏幕保护程序(screen saver)看似密集地使用了所有 CPU 时间。如果这个屏幕保护程序启动时你正在进行系统备份,备份操作会慢下来, 像蜗牛一样。但如果屏幕保护程序使用IDLE_PRIORITY_CLASS,它就只会在 CPU 绝对空闲的时候才执行。
最后一个类别是 REALTIME_PRIORITY_CLASS。这个类别用以协助解决一些和时间有密切关系的工作。举个例子,如果有个程序必须反应一个设备驱动程序的行为,而该驱动程序用来实时监控(real-time monitoring)真实世界中的一台仪器,那么将该进程设为这个优先权类别,就可以使它甚至优于核心进程和设备驱动程序。这个优先权类别不应该用于标准 GUI 程序或甚至于典型的服务器程序。
优先权类别适用于进程而非线程。你可以利用 SetPriorityClass() 和GetPriorityClass() 来调整和验证其值。本书并未涵盖这两个函数的说明。
优先权层级(Priority Level)
线程的优先权层级(Priority Level)是对进程的优先权类别的一个修改,使你能够调整同一个进程内的各线程的相对重要性。一共有七种优先权层级,显示于表格5-2 中。
表格5-2 优先权层级(Priority Levels)
优先权层级(Priority Levels) 调整值
THREAD_PRIORITY_HIGHEST +2
THREAD_PRIORITY_ABOVE_NORMAL +1
THREAD_PRIORITY_NORMAL 0
THREAD_PRIORITY_BELOW_NORMAL –1
THREAD_PRIORITY_LOWEST –2
THREAD_PRIORITY_IDLE Set to 1
THREAD_PRIORITY_TIME_CRITICAL Set to 15
注意:对于 REALTIME_PRIORITY_CLASS 的调整值,有点不同于上表所列。
优先权层级可以利用 SetThreadPriority() 改变之。
BOOL SetThreadPriority(
HANDLE hThread,
int nPriority
);
参数
hThread 代表欲调整优先权的那个线程。
nPriority 表格5-2 所显示的数值。
返回值
如果函数成功,就传回表格5-2 所列的其中一个值。如果函数失败,就传回 FALSE。GetLastError() 可以获得更详细的信息。
线程目前的优先权层级可以利用 GetThreadPriority() 获知。
int GetThreadPriority(
HANDLE hThread
);
参数
hThread 代表一个线程
返回值
如果函数成功, 就传回 TRUE 。如果函数失败, 就传回THREAD_PRIORITY_ERROR_RETURN。GetLastError() 可以获得更详细的信息。
KERNEL32.DLL 中的优先权
我使用 Windows 95 所提供的 PVIEW32,观察我的系统中的各个进程,结果如图5-1 所示。我看到系统模块 KERNEL32.DLL 有八个线程,其优先权类别是 HIGH_PRIORITY_CLASS,所以其基础优先权值为 13。检查其线程,发现有四个线程的优先权层级是 THREAD_PRIORITY_LOWEST,所以其优先权为11。三个线程的优先权层级是 THREAD_PRIORITY_NORMAL,所以其优先权为 13。一个线程的优先权层级是 THREAD_PRIORITY_TIME_CRITICAL,所以其优先权为 15。最后这个线程应该总是能够在任何其他“非实时线程”之前被调度程序选中执行。
图5-1 Windows 95 中的PVIEW32(图因win版本过旧省略)
动态提升(Dynamic Boost)
决定线程真正优先权的最后一个因素是其目前的动态提升值(Dynamic Boost)。所谓动态提升是对优先权的一种调整,使系统能够机动对待线程,以强化程序的可用性。
最容易被我们观察的,便是 Windows NT 施行于所有前台程序的“线程动态提升”。图5-2 的 系统属性 中的【性能】附页,允许用户指定前台程序应该对用户有怎样的回应。你可以在【我的电脑】中按下右键,并选择【属性】而获得这一画面。
默认情况下图5-2 的“动态提升”被设定为最大,这使得拥有键盘焦点的程序(前台程序)的优先权得以提升 +2。这个设定使得前台程序比后台程序获得较多的 CPU 时间,因此即使系统忙碌,前台程序还是容易保持其 UI 敏感度。
图5-2 Windows NT 4.0 的系统属性(图因win版本过旧省略)
第二种优先权动态提升也适用于同属一个进程的线程,用以反应用户的输入或磁盘的输入。例如,只要线程获得键盘输入,该线程就得到一个 +5 的优先权调整值。这使得该线程有机会处理那个输入,并且提供立即的回应给用户。其他可能引起优先权动态提升的情况还包括鼠标消息、计时器消息等等。
最后一种优先权动态提升的情况可能发生在任何一个线程(不限属于哪一个进程)身上。那是在一个“等待状态”获得满足时发生的,例如有一个线程正在等待一个 mutex,当 Wait...() 返回时,该线程的优先权会获得动态提升。这样的提升意味着 critical sections 将尽可能地被快速处理,而等待时间将尽可能地缩短。
更令人战栗的 Busy Waiting
你已经在第2章看到了,一个 busy loop 是如何地吃掉 CPU 时间。一旦你开始调整线程优先权,情况有可能变得更糟。书附盘片中有一个程序名为BUSYPRIO , 以 THREAD_PRIORITY_HIGHEST 来运行主线程, 以THREAD_PRIORITY_NORMAL 来运行 worker 线程。
如果你执行这个程序,你可能会看到一些非所期望的结果:程序永远结束不了。为什么?主线程不断等待,所以不断需要 CPU 时间。而由于它的优先权比 worker 线程高,所以 worker 线程永远没有机会获得 CPU 时间。这种情况称为 starvation(饥饿)。
BUSYPRIO 显示,小心翼翼地设定线程优先权是件多么重要的事情。改变线程优先权可能会打开潘朵拉的盒子,一些新的问题跑出来,死锁的阴影也潜在性地酝酿着。虽然优先权的基础知识很简单,但其实用面却可能很复杂。如果你的目标是保持简单,那就还是避免处理“优先权”这个烫手山芋吧。
第5章 不要让线程成为脱缰的野马(Keeping your Threads on Leash) ---线程优先权(Thread priority)的更多相关文章
- 第5章 不要让线程成为脱缰的野马(Keeping your Threads on Leash) ----初始化一个线程
使用线程的一个常见问题就是如何能够在一个线程开始运行之前,适当地将它初始化.初始化最常见的理由就是为了调整优先权.另一个理由是为了在SMP 系统中设定线程比较喜欢的 CPU.第10 章谈到 MFC 时 ...
- 第5章 不要让线程成为脱缰的野马(Keeping your Threads on Leash) ---干净的终止一个线程
干净的终止一个线程 我曾经在第2章产生一个后台线程,用以输出一张屏幕外的 bitmap 图.我们必须解决的一个最复杂的问题就是,如果用户企图结束程序,而这张bitmap 图尚未完成,怎么办?第2章的 ...
- 第5章 不要让线程成为脱缰的野马(Keeping your Threads on Leash) ---简介
这一章描述如何初始化一个新线程,如何停止一个执行中的线程,以及如何了解并调整线程优先权. 读过这一章之后,你将有能力回答一个 Win32 多线程程序设计的最基本问题.你一定曾经在 Usenet ...
- 第11章 Windows线程池(2)_Win2008及以上的新线程池
11.2 Win2008以上的新线程池 (1)传统线程池的优缺点: ①传统Windows线程池调用简单,使用方便(有时只需调用一个API即可) ②这种简单也带来负面问题,如接口过于简单,无法更多去控制 ...
- 线程操作案例--生产者与消费者,Object类对线程的支持
本章目标 1)加深对线程同步的理解 2)了解Object类中对线程的支持方法. 实例 生产者不断生产,消费者不断消费产品. 生产者生产信息后将其放到一个区域中,之后消费者从区域中取出数据. 既然生产的 ...
- Linux线程的实现 & LinuxThread vs. NPTL & 用户级内核级线程 & 线程与信号处理
另,线程的资源占用可见:http://www.cnblogs.com/charlesblc/p/6242111.html 进程 & 线程的很多知识可以看这里:http://www.cnblog ...
- java笔记--用ThreadLocal管理线程,Callable<V>接口实现有返回值的线程
用ThreadLocal管理线程,Callable<V>接口实现有返回值的线程 ThreadLocal在我的笔记"关于线程同步"的第5种方式里面有介绍,这里就不多说了. ...
- 线程间操作无效: 从不是创建控件“”的线程访问它~~~的解决方法~ 线程间操作无效: 从不是创建控件“Control Name'”的线程访问它问题的解决方案及原理分析
看两个例子,一个是在一个进程里设置另外一个进程中控件的属性.另外一个是在一个进程里获取另外一个进程中控件的属性. 第一个例子 最近,在做一个使用线程控制下载文件的小程序(使用进度条控件显示下载进度)时 ...
- Tomcat线程池,更符合大家想象的可扩展线程池
因由 说起线程池,大家可能受连接池的印象影响,天然的认为,它应该是一开始有core条线程,忙不过来了就扩展到max条线程,闲的时候又回落到core条线程,如果还有更高的高峰,就放进一个缓冲队列里缓冲一 ...
随机推荐
- 一、Nginx安装手册
1 nginx安装环境 nginx是C语言开发,建议在linux上运行,本教程使用Centos6.5作为安装环境. gcc 安装nginx需要先将官网下载的源码进行编译,编译依赖gcc环境,如果没有g ...
- vim代码粘贴缩进混乱的问题[Linux]
详见: http://blog.yemou.net/article/query/info/tytfjhfascvhzxcytp76 直接在vim插入模式下粘贴: 直接粘贴,剪贴板上的每个字符都相当 ...
- ROS学习记录(一)————创建简单的机器人模型smartcar
这是我在古月居上找的(http://www.guyuehome.com/243),但直接运行的话,没办法跑起来,我也是查了好多博客和日志,才实现最后的功能的,所以,记录下来,以备后用吧,也欢迎其他和我 ...
- mkyaffs2image制作根文件系统、使用NFS挂载虚拟机目录(2)
1.制作根文件系统及nfs烧写 1.1 先解压文件系统,/wok/nfs_root 目录下是已经构造好的各种文件系统:① fs_mini.tar.bz2 是最小的根文件系统,里面的设备节点是事先建立好 ...
- 正则语言引擎:一个简单LEX和YACC结合运用的实例
本文先描述了LEX与YACC的书写方法.然后利用LEX与YACC编写了一个简单正则语言的引擎(暂时不支持闭包与或运算),生成的中间语言为C语言. 正则引擎应直接生成NFA或DFA模拟器的输入文件,但在 ...
- 自制ACL+DHCP实验(初版)
(实验用gns模拟器) ACL 实验拓扑: 实验要求: 1.1.1.1→3.3.3.3 不通 11.11.11.11→3.3.3.3 通 2.2.2.2→3.3.3.3 通 实验步骤: 步骤一:基本配 ...
- 第二次作业:结对编程,四则运算的GUI实现
小伙伴:201421123031 余洋 201421123044 潘志坚 题目要求: 我们在个人作业1中,用各种语言实现了一个命令行的四则运算小程序.进一步,本次要求把这个程序做成GUI(可以是W ...
- 【Beta】Daily Scrum Meeting——Day4
站立式会议照片 1.本次会议为第四次Meeting会议: 2.本次会议在早上9:35,在陆大2楼机房召开,本次会议为25分钟讨论今天要完成的任务以及接下来的任务安排. 燃尽图 每个人的工作分配 成 员 ...
- 在配置github中遇到的一些问题
这次在配置github时,我出现了问题,就是在我装好Git以后,我打开Git Bash,输入了这句代码:$ ssh-keygen -t rsa -C "your_email@youremai ...
- 第02周-Java作业评价
1. 本周作业简评与建议 作业存在的问题 格式上还是存在问题,但是比较第一次有很大的进步. 答题上,有的同学还是很敷衍,题目要求有分析过程,但是只写一个false,true,没有分析.也比较缺少自己的 ...