检测到CPU 饥饿.当前java线程调度度延迟X秒.求助

理解&Thread.Sleep&函数&,Sleep(0)&释放当前线程所剩余的时间片,让线程马上回
&在线程中,调用sleep(0)可以释放cpu时间,让线程马上重新回到就绪队列而非等待队列,sleep(0)释放当前线程所剩余的时间片(如果有剩余的话),这样可以让操作系统切换其他线程来执行,提升效率。
究其原理,笔者在网上搜了一下,发现这篇文章;
我们可能经常会用到 Thread.Sleep
函数来使线程挂起一段时间。那么你有没有正确的理解这个函数的用法呢?思考下面这两个问题:
假设现在是
12:00:00.000,如果我调用一下 Thread.Sleep(1000) ,在
12:00:01.000
的时候,这个线程会 不会被唤醒?
某人的代码中用了一句看似莫明其妙的话:Thread.Sleep(0) 。既然是
Sleep 0 毫秒,那么他跟去掉这句代码相比,有啥区别么?
我们先回顾一下操作系统原理。
操作系统中,CPU竞争有很多种策略。Unix系统使用的是时间片算法,而Windows则属于抢占式的。
在时间片算法中,所有的进程排成一个队列。操作系统按照他们的顺序,给每个进程分配一段时间,即该进程允许运行的时间。如果在
时间片结束时进程还在运行,则CPU将被剥夺并分配给另一个进程。如果进程在时间片结束前阻塞或结束,则CPU当即进行切换。调度程
序所要做的就是维护一张就绪进程列表,,当进程用完它的时间片后,它被移到队列的末尾。
所谓抢占式操作系统,就是说如果一个进程得到了 CPU 时间,除非它自己放弃使用 CPU
,否则将完全霸占 CPU 。因此可以看出,在抢 占式操作系统中,操作系统假设所有的进程都是“人品很好”的,会主动退出 CPU
在抢占式操作系统中,假设有若干进程,操作系统会根据他们的优先级、饥饿时间(已经多长时间没有使用过
CPU 了),给他们算出一 个总的优先级来。操作系统就会把 CPU
交给总优先级最高的这个进程。当进程执行完毕或者自己主动挂起后,操作系统就会重新计算一 次所有进程的总优先级,然后再挑一个优先级最高的把
CPU 控制权交给他。
我们用分蛋糕的场景来描述这两种算法。假设有源源不断的蛋糕(源源不断的时间),一副刀叉(一个CPU),10个等待吃蛋糕的人(10
个进程)。
如果是 Unix操作系统来负责分蛋糕,那么他会这样定规矩:每个人上来吃 1
分钟,时间到了换下一个。最后一个人吃完了就再从头开始。于是,不管这10个人是不是优先级不同、饥饿程度不同、饭量不同,每个人上来的时候都可以吃
分钟。当然,如果有人本来不太饿,或者饭量小,吃了30秒钟之后就吃饱了,那么他可以跟操作系统说:我已经吃饱了(挂起)。于是操作系统就会让下一个人接着来。
如果是 Windows
操作系统来负责分蛋糕的,那么场面就很有意思了。他会这样定规矩:我会根据你们的优先级、饥饿程度去给你们每个人计算一个优先级。优先级最高的那个人,可以上来吃蛋糕——吃到你不想吃为止。等这个人吃完了,我再重新根据优先级、饥饿程度来计算每个人的优先级,然后再分给优先级最高的那个人。
这样看来,这个场面就有意思了——可能有些人是PPMM,因此具有高优先级,于是她就可以经常来吃蛋糕。可能另外一个人是个丑男,而去很ws,所以优先级特别低,于是好半天了才轮到他一次(因为随着时间的推移,他会越来越饥饿,因此算出来的总优先级就会越来越高,因此总有一天会轮到他的)。而且,如果一不小心让一个大胖子得到了刀叉,因为他饭量大,可能他会霸占着蛋糕连续吃很久很久,导致旁边的人在那里咽口水。。。
而且,还可能会有这种情况出现:操作系统现在计算出来的结果,5号PPMM总优先级最高,而且高出别人一大截。因此就叫5号来吃蛋糕。5号吃了一小会儿,觉得没那么饿了,于是说“我不吃了”(挂起)。因此操作系统就会重新计算所有人的优先级。因为5号刚刚吃过,因此她的饥饿程度变小了,于是总优先级变小了;而其他人因为多等了一会儿,饥饿程度都变大了,所以总优先级也变大了。不过这时候仍然有可能5号的优先级比别的都高,只不过现在只比其他的高一点点——但她仍然是总优先级最高的啊。因此操作系统就会说:5号mm上来吃蛋糕……(5号mm心里郁闷,这不刚吃过嘛……人家要减肥……谁叫你长那么漂亮,获得了那么高的优先级)。
那么,Thread.Sleep
函数是干吗的呢?还用刚才的分蛋糕的场景来描述。上面的场景里面,5号MM在吃了一次蛋糕之后,觉得已经有8分饱了,她觉得在未来的半个小时之内都不想再来吃蛋糕了,那么她就会跟操作系统说:在未来的半个小时之内不要再叫我上来吃蛋糕了。这样,操作系统在随后的半个小时里面重新计算所有人总优先级的时候,就会忽略5号mm。Sleep函数就是干这事的,他告诉操作系统“在未来的多少毫秒内我不参与CPU竞争”。
看完了 Thread.Sleep
的作用,我们再来想想文章开头的两个问题。
对于第一个问题,答案是:不一定。因为你只是告诉操作系统:在未来的1000毫秒内我不想再参与到CPU竞争。那么1000毫秒过去之后,这时候也许另外一个线程正在使用CPU,那么这时候操作系统是不会重新分配CPU的,直到那个线程挂起或结束;况且,即使这个时候恰巧轮到操作系统进行CPU
分配,那么当前线程也不一定就是总优先级最高的那个,CPU还是可能被其他线程抢占去。
与此相似的,Thread有个Resume函数,是用来唤醒挂起的线程的。好像上面所说的一样,这个函数只是“告诉操作系统我从现在起开始参与CPU竞争了”,这个函数的调用并不能马上使得这个线程获得CPU控制权。
对于第二个问题,答案是:有,而且区别很明显。假设我们刚才的分蛋糕场景里面,有另外一个PPMM
7号,她的优先级也非常非常高(因为非常非常漂亮),所以操作系统总是会叫道她来吃蛋糕。而且,7号也非常喜欢吃蛋糕,而且饭量也很大。不过,7号人品很好,她很善良,她没吃几口就会想:如果现在有别人比我更需要吃蛋糕,那么我就让给他。因此,她可以每吃几口就跟操作系统说:我们来重新计算一下所有人的总优先级吧。不过,操作系统不接受这个建议——因为操作系统不提供这个接口。于是7号mm就换了个说法:“在未来的0毫秒之内不要再叫我上来吃蛋糕了”。这个指令操作系统是接受的,于是此时操作系统就会重新计算大家的总优先级——注意这个时候是连7号一起计算的,因为“0毫秒已经过去了”嘛。因此如果没有比7号更需要吃蛋糕的人出现,那么下一次7号还是会被叫上来吃蛋糕。
因此,Thread.Sleep(0)的作用,就是“触发操作系统立刻重新进行一次CPU竞争”。竞争的结果也许是当前线程仍然获得CPU控制权,也许会换成别的线程获得CPU控制权。这也是我们在大循环里面经常会写一句Thread.Sleep(0)
,因为这样就给了其他线程比如Paint线程获得CPU控制权的权力,这样界面就不会假死在那里。
末了说明一下,虽然上面提到说“除非它自己放弃使用 CPU ,否则将完全霸占
CPU”,但这个行为仍然是受到制约的——操作系统会监控你霸占CPU的情况,如果发现某个线程长时间霸占CPU,会强制使这个线程挂起,因此在实际上不会出现“一个线程一直霸占着
不放”的情况。至于我们的大循环造成程序假死,并不是因为这个线程一直在霸占着CPU。实际上在这段时间操作系统已经进行过多次CPU竞争了,只不过其他线程在获得CPU控制权之后很短时间内马上就退出了,于是就又轮到了这个线程继续执行循环,于是就又用了很久才被操作系统强制挂起。。。因此反应到界面上,看起来就好像这个线程一直在霸占着CPU一样。
末了再说明一下,文中线程、进程有点混乱,其实在Windows原理层面,CPU竞争都是线程级的,本文中把这里的进程、线程看成同一个东西就好了。
已投稿到:
以上网友发言只代表其个人观点,不代表新浪网的观点或立场。2682人阅读
&在线程中,调用sleep(0)可以释放cpu时间,让线程马上重新回到就绪队列而非等待队列,sleep(0)释放当前线程所剩余的时间片(如果有剩余的话),这样可以让操作系统切换其他线程来执行,提升效率。
究其原理,笔者在网上搜了一下,发现这篇文章;
我们可能经常会用到 Thread.Sleep 函数来使线程挂起一段时间。那么你有没有正确的理解这个函数的用法呢?思考下面这两个问题:
假设现在是
12:00:00.000,如果我调用一下 Thread.Sleep(1000) ,在
12:00:01.000 的时候,这个线程会 不会被唤醒?某人的代码中用了一句看似莫明其妙的话:Thread.Sleep(0) 。既然是 Sleep 0 毫秒,那么他跟去掉这句代码相比,有啥区别么?
我们先回顾一下操作系统原理。
操作系统中,CPU竞争有很多种策略。Unix系统使用的是时间片算法,而Windows则属于抢占式的。
在时间片算法中,所有的进程排成一个队列。操作系统按照他们的顺序,给每个进程分配一段时间,即该进程允许运行的时间。如果在 时间片结束时进程还在运行,则CPU将被剥夺并分配给另一个进程。如果进程在时间片结束前阻塞或结束,则CPU当即进行切换。调度程 序所要做的就是维护一张就绪进程列表,,当进程用完它的时间片后,它被移到队列的末尾。
所谓抢占式操作系统,就是说如果一个进程得到了 CPU 时间,除非它自己放弃使用 CPU ,否则将完全霸占 CPU 。因此可以看出,在抢 占式操作系统中,操作系统假设所有的进程都是“人品很好”的,会主动退出 CPU 。
在抢占式操作系统中,假设有若干进程,操作系统会根据他们的优先级、饥饿时间(已经多长时间没有使用过 CPU 了),给他们算出一 个总的优先级来。操作系统就会把 CPU 交给总优先级最高的这个进程。当进程执行完毕或者自己主动挂起后,操作系统就会重新计算一 次所有进程的总优先级,然后再挑一个优先级最高的把 CPU 控制权交给他。
我们用分蛋糕的场景来描述这两种算法。假设有源源不断的蛋糕(源源不断的时间),一副刀叉(一个CPU),10个等待吃蛋糕的人(10 个进程)。
如果是 Unix操作系统来负责分蛋糕,那么他会这样定规矩:每个人上来吃 1 分钟,时间到了换下一个。最后一个人吃完了就再从头开始。于是,不管这10个人是不是优先级不同、饥饿程度不同、饭量不同,每个人上来的时候都可以吃 1 分钟。当然,如果有人本来不太饿,或者饭量小,吃了30秒钟之后就吃饱了,那么他可以跟操作系统说:我已经吃饱了(挂起)。于是操作系统就会让下一个人接着来。
如果是 Windows 操作系统来负责分蛋糕的,那么场面就很有意思了。他会这样定规矩:我会根据你们的优先级、饥饿程度去给你们每个人计算一个优先级。优先级最高的那个人,可以上来吃蛋糕——吃到你不想吃为止。等这个人吃完了,我再重新根据优先级、饥饿程度来计算每个人的优先级,然后再分给优先级最高的那个人。
这样看来,这个场面就有意思了——可能有些人是PPMM,因此具有高优先级,于是她就可以经常来吃蛋糕。可能另外一个人是个丑男,而去很ws,所以优先级特别低,于是好半天了才轮到他一次(因为随着时间的推移,他会越来越饥饿,因此算出来的总优先级就会越来越高,因此总有一天会轮到他的)。而且,如果一不小心让一个大胖子得到了刀叉,因为他饭量大,可能他会霸占着蛋糕连续吃很久很久,导致旁边的人在那里咽口水。。。
而且,还可能会有这种情况出现:操作系统现在计算出来的结果,5号PPMM总优先级最高,而且高出别人一大截。因此就叫5号来吃蛋糕。5号吃了一小会儿,觉得没那么饿了,于是说“我不吃了”(挂起)。因此操作系统就会重新计算所有人的优先级。因为5号刚刚吃过,因此她的饥饿程度变小了,于是总优先级变小了;而其他人因为多等了一会儿,饥饿程度都变大了,所以总优先级也变大了。不过这时候仍然有可能5号的优先级比别的都高,只不过现在只比其他的高一点点——但她仍然是总优先级最高的啊。因此操作系统就会说:5号mm上来吃蛋糕……(5号mm心里郁闷,这不刚吃过嘛……人家要减肥……谁叫你长那么漂亮,获得了那么高的优先级)。
那么,Thread.Sleep 函数是干吗的呢?还用刚才的分蛋糕的场景来描述。上面的场景里面,5号MM在吃了一次蛋糕之后,觉得已经有8分饱了,她觉得在未来的半个小时之内都不想再来吃蛋糕了,那么她就会跟操作系统说:在未来的半个小时之内不要再叫我上来吃蛋糕了。这样,操作系统在随后的半个小时里面重新计算所有人总优先级的时候,就会忽略5号mm。Sleep函数就是干这事的,他告诉操作系统“在未来的多少毫秒内我不参与CPU竞争”。
看完了 Thread.Sleep 的作用,我们再来想想文章开头的两个问题。
对于第一个问题,答案是:不一定。因为你只是告诉操作系统:在未来的1000毫秒内我不想再参与到CPU竞争。那么1000毫秒过去之后,这时候也许另外一个线程正在使用CPU,那么这时候操作系统是不会重新分配CPU的,直到那个线程挂起或结束;况且,即使这个时候恰巧轮到操作系统进行CPU 分配,那么当前线程也不一定就是总优先级最高的那个,CPU还是可能被其他线程抢占去。
与此相似的,Thread有个Resume函数,是用来唤醒挂起的线程的。好像上面所说的一样,这个函数只是“告诉操作系统我从现在起开始参与CPU竞争了”,这个函数的调用并不能马上使得这个线程获得CPU控制权。
对于第二个问题,答案是:有,而且区别很明显。假设我们刚才的分蛋糕场景里面,有另外一个PPMM 7号,她的优先级也非常非常高(因为非常非常漂亮),所以操作系统总是会叫道她来吃蛋糕。而且,7号也非常喜欢吃蛋糕,而且饭量也很大。不过,7号人品很好,她很善良,她没吃几口就会想:如果现在有别人比我更需要吃蛋糕,那么我就让给他。因此,她可以每吃几口就跟操作系统说:我们来重新计算一下所有人的总优先级吧。不过,操作系统不接受这个建议——因为操作系统不提供这个接口。于是7号mm就换了个说法:“在未来的0毫秒之内不要再叫我上来吃蛋糕了”。这个指令操作系统是接受的,于是此时操作系统就会重新计算大家的总优先级——注意这个时候是连7号一起计算的,因为“0毫秒已经过去了”嘛。因此如果没有比7号更需要吃蛋糕的人出现,那么下一次7号还是会被叫上来吃蛋糕。
因此,Thread.Sleep(0)的作用,就是“触发操作系统立刻重新进行一次CPU竞争”。竞争的结果也许是当前线程仍然获得CPU控制权,也许会换成别的线程获得CPU控制权。这也是我们在大循环里面经常会写一句Thread.Sleep(0) ,因为这样就给了其他线程比如Paint线程获得CPU控制权的权力,这样界面就不会假死在那里。
末了说明一下,虽然上面提到说“除非它自己放弃使用 CPU ,否则将完全霸占 CPU”,但这个行为仍然是受到制约的——操作系统会监控你霸占CPU的情况,如果发现某个线程长时间霸占CPU,会强制使这个线程挂起,因此在实际上不会出现“一个线程一直霸占着 CPU 不放”的情况。至于我们的大循环造成程序假死,并不是因为这个线程一直在霸占着CPU。实际上在这段时间操作系统已经进行过多次CPU竞争了,只不过其他线程在获得CPU控制权之后很短时间内马上就退出了,于是就又轮到了这个线程继续执行循环,于是就又用了很久才被操作系统强制挂起。。。因此反应到界面上,看起来就好像这个线程一直在霸占着CPU一样。
末了再说明一下,文中线程、进程有点混乱,其实在Windows原理层面,CPU竞争都是线程级的,本文中把这里的进程、线程看成同一个东西就好了。
原文章连接:
参考知识库
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
访问:21515次
排名:千里之外
转载:13篇
(1)(5)(13)您所在位置: &
&nbsp&&nbsp&nbsp&&nbsp
基于通用CPU的GPRS核心网的设计与实现.pdf75页
本文档一共被下载:
次 ,您可免费全文在线阅读后下载本文档
文档加载中...广告还剩秒
需要金币:200 &&
你可能关注的文档:
··········
··········
近年来,各专业领域用户对于行业集群通信的需求日益增多,集群通信
方便、快捷和信道资源共享等特点使其迅速发展。数字集群通信具备兼容性、
开放性、保密性强、频谱利用率高等特点成为集群通信的发展趋势和研究热
点,但其投资成本高。因此研究具有低成本、便携性和实用性的集群通信系
统具有非常重要的意义。
本文在学习GPRS系统原理、体系结构和GPRS核心网业务的信令流程
基础上,深入研究了GPRS系统的网络结构、网络接口和GPRS网络单元和
接口,包括网络单元有分组控制单元PCU PacketUIlit 、GPRS服
Node 和网关GPRS支持节点
务支持节点SGSN SeⅣirlgSupport
GPRS Node 。
GGSN GatewaySupport
进行GPRS系统整体设计。遵循3GPP标准,首先对GPRS系统整体框
架和系统模块单元架构进行了设计,并在通用CPU上集成设计实现了GPRS
会话管理功能的信令流程和网络整体配置,该系统具有通用性和高效性等特
点,可大大节省系统的硬件资源,降低系统能耗。由于GTP协议不支持SGSN
与GGSN在同一PC机上运行GTP实体,本文对GTP协议中用户面与数据
面的通信端口重新进行设计和实现,提高了系统的可移植性。在GGsN网
关实现中,增加了口回收机制,通过使用IP回收池提高了P资源可利用
最后搭建了基于通用CPU的GPI峪测试实验平台,对GPRS系统进行
了模块测试和集成测试,检测每个模块和重要信令流程的正确性和鲁棒性。
测试结果表明移动终端可通过GPRS与№nlet进行通信,并通过多次重复
性测试验证了系统的稳定性。
正在加载中,请稍后...操作系统(9)
CPU调度&&&用于多道程序
以下先讨论对于单CPU的调度问题。
回顾多道程序,同时把多个进程导入内存,使得一个进程在CPU中执行I/O时,一个进程用来填补CPU的时间。
通常进程都是在CPU区间和I/O区间之间转换。
CPU调度程序称为短期调度程序,从内存调度到CPU。
在内存中等待的就绪队列的节点是PCB。有许多不同的队列实现方法。
抢占调度和非抢占调度(协作):前者为一个进程还没结束之前就被夺取CPU的拥有权,而后者则要一个进程结束或等待I/O才给予其他进程CPU的拥有权。
虽然现代操作系统都是用抢占调度,但是对于同时访问一个数据来说就会有风险,比如一个进程在试图更新一个数据,但是另一个进程抢占,并且读取这个数据,使得数据不一致。这时就要用到进程同步的内容。lock
对于中断随时可能发生的情况,我们可以在执行某个代码段时,禁止中断。
分派程序用来把CPU的拥有权交给短期调度程序选定的进程。每次切换进程时都要使用。
分派延迟:分派程序所花的时间。
CPU调度需要考虑的因素:
1.CPU使用率。
2.吞吐量:单位时间完成进程的数量。
3.周转时间:进程提交到进程完成。即从磁盘等待进入内存+就绪队列等待时间+CPU执行时间+I/O执行时间。但是CPU调度算法只是里面的一块。
4.等待时间:在就绪队列等待的时间之和。
5.响应时间:用于交互系统。
CPU调度算法:&&&&&Gantt图考点求等待时间
此算法应用于内存就绪队列到CPU的过程。
1.FCFS&先到先服务
一旦选定进程,那么在结束之前就不能再切换到另一个进程。
2.SJF&最短优先&&精确的讲是最短下一个CPU区间的算法
前面提到,一个进程是由CPU区间和I/O区间交替组成的。而SJF是看哪个进程的CPU区间最短。
(1)SRTF抢占式:又称最短剩余优先,当新进来的进程的CPU区间比当前执行的进程所剩的CPU区间短,则抢占。
(2)非抢占:称为下一个最短优先,因为在就绪队列中选择最短CPU区间的进程放在队头。
SJF用于长期调度而不能用短期调度,因为进程是一个整体,CPU没法知道进程中第一个CPU区间长度。
SJF需要确定下一个CPU区间的时间长度,可以通过近似估算出下一个CPU区间的长度,比如tn+1=atn+(1-a)rn&&tn为最近最近一次的CPU时间,rn为历史记录。a是给定的权重。
3.优先级调度算法&&pintos的优先级是0-63&&0为最低优先级,63为最高优先级
SJF是特殊的优先级调度算法,以CPU区间长度的倒数为优先级。
(1)内部优先级:通过内部数据比如内存要求等。
(2)外部优先级:用户自己设定。set_priority
分为抢占式和非抢占式,前者为如果进来的进程优先级高于运行的进程,则替换;后者只是在就绪队列中按优先级排队。
缺点:无线阻塞或饥饿。前者为一个优先级高且运行时间长的进程一直阻塞,后者为优先级低的进程永远都得不到执行。
解决饥饿的方法是老化。通过每个时间间隔后将等待的进程优先级降低。
4.转轮法&RR算法&抢占式
用于分时系统。每个进程都占用一个时间片的时间。就绪队列为FIFO循环队列。如果一个进程的CPU区间长度小于时间片,则继续下面的进程;如果大于时间片,则中断切换到下一个进程执行。
通常时间片长度为10ms-100ms,由此需要确定时间片大小使得上下文切换次数适当少。
5.多级队列调度
根据某种性质将一个就绪队列分成不同的独立队列,如系统进程,交互进程(前台进程),交互编辑进程,批处理进程,学生进程。
每个队列都有不同的调度算法。
每个队列都有优先级,比如前台队列就比后台队列要有绝对的优先级,因此队列间的分配方法:
(1)只有优先级高的队列为空,才能执行低优先级队列。
(2)为队列分配不同权重的CPU时间,优先级高的分配时间多。
6.多级反馈队列&&&抢占式
动态调整进程,进程在不同队列之间移动,虽然在队列间移动需要耗费资源,但是更合理。
按照CPU区间的大小分队列。
进程之间的划分是按照所花CPU时间划分,比如队列0是就绪队列,且规定一个时间上界,如果一个进程没能规定时间完成,则被放入队列1中。CPU区间越大的进程就被放入低优先级中。每个进程一开始都进入就绪队列。
多级反馈队列的参数:
1.队列的数量。
2.每个队列的调度算法。
3.怎样升级到优先级更高的队列。
4.确定怎样降级到优先级更低的队列。
5.进程需要确定进入哪个队列。
接下来讲多个CPU的负载均衡问题。
假设多个CPU是同构的,但是可能也会有特殊的限制比如只有某个CPU与I/O设备连接。
(1)非对称多处理:一个处理器专门用于CPU调度决定等,其他用于执行用户代码。
(2)对称多处理(SMP):为每个处理器自我调度,可能会造成多个处理器同时访问同一个数据结构则会造成冲突。
处理器亲和性:一个进程只需要在一个处理器上执行即可,不会转到另一个处理器上执行,因为如果转移的话,处理器缓存的资源全部无效,浪费。缓存存储的是进程的连续访问的数据。
软亲和性:占时的不会转移。
硬亲和性:操作系统不允许进程在多处理器间游走。
负载平衡条件:每个处理器都有私有的就绪队列。
负载平衡方法:push和pull。即从负载高的处理器push到低负载的处理器上,从负载低的处理器pull到负载高的处理器,但是这样就缺失了处理器亲和性。
一个物理处理器可以划分为逻辑处理器,SMT(对称多线程)使得在一个物理处理器上同时运行多个线程。
逻辑处理器对于物理处理器就像线程对进程。多个逻辑处理器共享物理处理器的资源,如缓存和总线。
举个例子,就像分区一样,硬盘分为C盘,D盘等,但事实上不是真的分硬盘。更理论的讲,像数据库的逻辑和物理关系。
系统调度的是内核线程,用户线程由线程库管理。如果线程要在CPU上运行,需要与某个内核线程相连。
用户线程需要连接到LWP(进程竞争范围PCS)。
内核线程连接到物理CPU(系统竞争范围SCS)。
linux采用抢占、优先级的调度算法,较高优先级的进程被分配较多的CPU时间片。每个处理器都维护一个运行队列。运行队列分为活动和到期的,前者是进程所耗时间小于时间片的,后者是所花时间大于时间片的任务。
当活动队列为空,则互换两队列。
调度算法的评估:
1.分析评估法。事先确定负荷和算法,即一些本来可以自己设定的数据,比如确定特定算法FCFS,确定进程到来的时间和数量;根据不同的模型来比较性能。缺点是只适用于特定的情况。
2.排队模型。数学公式以分析CPU和I/O的区间分布,给定进程到达系统的时间分布,排队网络分析。LITTLE公式:进入队列的进程和离开队列的进程要相等。
3.模拟。建模计算机系统,模拟程序,根据概率分布随机生成数据,不能对于前后事件进行预测。但是通过跟踪磁带来记录真实系统的运作,再来按照这种顺序来模拟即可。
4.实现。编程后放入操作系统,观测。
参考知识库
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
访问:36691次
排名:千里之外
原创:32篇
转载:112篇
(1)(1)(6)(22)(32)(7)(10)(23)(4)(24)(5)(9)如何获得十亿分之一秒的时钟精度
要想真正有效地测试、优化程序性能——特别是为Windows服务器开发的多线程程序,操作系统提供的标准时钟是不够的,必须使用解析度更高的时钟。本文介绍了如何访问处理器的十亿分之一秒级别的时钟,极大地提高代码性能测试的速度和精度。 一、获取计时数据 和其他Windows服务器一样,在Windows 2003 Server上最能发挥性能优势的是多线程程序。Windows 2003 Server支持各种多处理器系统,同时也能在单处理器的P4系统上运行。对于单处理器P4系统,Windows 2003 Server将发挥出Intel超线程技术提供的各种硬件线程执行引擎的优势。 开发服务器应用的人都知道,之所以要开发并行程序,真正的原因只有一个——性能。然而,众所周知,性能改善是一个比较模糊的目标,因为多线程代码的性能通常只能靠经验估计。在单线程程序中,性能改进程度一般可以精确地预知,例如减少了多少指令和延迟较高的操作,但多线程代码不同,Windows平台中线程调度是不确定的,也就是说,在Windows中应用程序可以要求调度程序运行线程,但调度程序何时(是否)运行线程则远远超出了应用程序代码的控制范围。 在测试性能时,开发者很快会遇到一个问题,这就是Windows内建的标准时钟实在不够精确,其可靠测量事件时间的解析度很难高于一秒,这样,要确定一个代码片段是否真正得到优化就很困难了。如果一定要用Windows的标准时钟进行测试,必须利用循环让代码运行几百万次,才能获得有效的时间数据。绝大多数情况下,使用这类循环意味着修改应用程序。 其实,还有更好的办法,这就是Win32高解析度时钟,涉及的函数有两个:QueryPerformanceCount(),QueryPerformanceFrequency()。在Intel系统中,从P II开始,这些函数依赖于Pentium芯片内建的一个计数器。当一个Intel系统启动时,一个64位的寄存器跟踪着消逝的时钟周期,这个计数器提供了解析度极高的计时设备。 整个64位寄存器都要用到。32 bit的整数大约能计数20亿,对于当前每秒运行20-30亿个周期的处理器,32 bit的计数器会在一秒或更少的时间内溢出,64 bit的计数器则能容纳这些秒数的20亿倍,按20亿秒计算就是约63年——可以相信,这已经远远超出测量任何程序的要求了。 要对一个事件进行计时,只需获得事件开始之前、结束之后的时钟计数。下面的代码不依赖于Win32(即,从C/C++直接访问),稍后我们再看看操作系统提供的函数。我们首先定义一个数据结构,然后再来看填写该结构的代码:
typedef struct _BinInt32 { __int32 i32[2]; } BigInt32; typedef struct _BigInt64 { __int64 i64; } BigInt64; typedef union _bigInt { BigInt32 int32 BigInt64 int64 } BigI
下面的代码从操作系统获得时钟计数器的高位和低位,分别填写__int64数据的两个32 bit部分:
BigInt start_ticks, end_ _asm { RDTSC mov start_ticks.int32val.i32[0], eax mov start_ticks.int32val.i32[4], edx }
这段代码能够在Visual Studio .NET 2003中顺利运行,在以前的C/C++编译器中也应该没有问题。RDTSC(ReaD Time Stamp Counter)是一个汇编指令,它的功能是把时间戳计数器的内容装入EAX和EDX寄存器。执行上述代码后,start_ticks就包含了完整的时钟计数。再次调用上面的代码,把start_ticks替换成end_ticks,再从end_ticks减去start_ticks,就得到了两次调用期间流逝的时钟周期。 要输出这个_int64值,可以使用下面的printf()掩码:
printf ( &Function used %I64Ld ticks/n&, end_ticks.int64val.i64 - start_ticks.int64val.i64 );
Win32函数QueryPerformanceCounter()的功能也大致相似,它唯一的参数是一个指向计数器变量的长指针。如果函数调用失败,它返回0(实际上是FALSE)。然而,上面提供的代码突破了Windows调用的黑箱,即使在非Windows的Intel系统上,也能发挥同样的功能。 二、使用计时数据 如果要把时钟计数转换成时间,只要把时钟计数除以CPU的时钟频率就可以了。不过,芯片上标准的GHz数据往往与实际运行的速度不同。如果要测试芯片的实际速度,除了Win32调用QueryPerformanceFrequency(),还有几种非常好的工具软件。这里要推荐两种工具,首先是Intel自己的Processor Frequency ID Utility,可以从/support/ processors/tools/FrequencyID/FreqID.htm免费下载,它还能提供有关处理器的许多其他信息。另一个工具提供的信息更多,它就是wCPUID,可以从http://www./免费下载。 这两种工具都能够测出精确的时钟速度,用前面获得的时钟计数除以速度,就可以得到高精度的时间计数。QueryPerformanceFrequency()函数也只有一个长指针参数,出现错误时返回0或FALSE。 在如此高的时钟解析度下,许多平常看不到的现象会显现出来。最令人莫名其妙的是,多次测试同一段代码,结果会出现很大的波动。 大范围波动的主要原因在于读取操作,特别是第一、二两次读取与从缓冲区读取的差异。当代码第一次执行时,一般需要把它装入到缓冲区,代码所操作的数据也一样。用时钟周期来度量,这一缓冲装入过程是相当耗时的。不过,当代码和数据放入了缓冲区(多次运行代码之后的结果),装入缓冲区操作所带来的失真渐渐消失。因此,实际测试时,应当抛弃前几次的数据,只计算结果稳定下来之后的平均值。 然而,即使在看起来比较稳定的结果集中,仍会突然出现一些突变,这是由于操作系统切换线程所导致的。由于时钟计数器总是不停地累加,它的计数不能反映出代码的一部分执行时间已经用于休眠。要解决这个问题,必须将线程设置成Windows最高的优先级,即实时(对应的符号是REALTIME_PRIORITY_CLASS),防止测试期间线程被切换掉。 但是,采用这种解决办法时必须谨慎。如果让一大段代码用这个优先级运行,可能会阻塞其他线程。因此,如果要用这种办法测试大段代码,应当确信系统暂时不作它用。另外,必须记住的是,测试完成后要把代码恢复成标准优先级——注意,是在测试完成后立即恢复,否则的话,可能带来许多风险,例如,可能直到部署应用程序时也不能再想起需要恢复优先级,由此带来的问题可能使用户久久难忘——当你的应用程序开始运行时,其他代码都好像停止运行了。 当然,这是一个可以暂且不管的话题。无论怎样,现在我们已经有了一个高度精确的时钟,它能够在大多数当前的Intel处理器上运行,适合从Windows 95开始的所有Windows操作系统。好好享受吧!
本分类共有文章17篇,更多信息详见
& 2012 - 2014 &
&All Rights Reserved. &
/*爱悠闲图+*/
var cpro_id = "u1888441";
/*爱悠闲底部960*75*/
var cpro_id = "u1888128";

我要回帖

更多关于 线程饥饿 的文章

 

随机推荐