WINDOWS核心编程学习心得--线程调度

时间:2021-12-28 16:26:48
线程的主要有五种状态

1.新建状态:新创建了一个线程对象。

2.就绪状态:线程对象创建后,其他线程调用了该对象的start()方法。该状态的线程位于可运行线程池中,变得可运行,等待获取CPU的使用权。

3.运行状态:就绪状态的线程获取了CPU,执行程序代码。

4.阻塞状态:阻塞状态是线程因为某种原因放弃CPU使用权,暂时停止运行。直到线程进入就绪状态,才有机会转到运行状态。

5.挂起状态:一般是指被挂起,因为同一时刻,需要“同步”运行的线程不止他一个,所以基于时间片轮转的原则,他在独占了一段时间的CPU后,被挂起,线程环境被压栈。

下面就挂起和阻塞进行以下说明:

1.挂起是一种主动行为,因此恢复也应该要主动完成,而阻塞则是一种被动行为,是在等待事件或资源时任务的表现,你不知道他什么时候被阻塞(pend),也就不能确切 的知道他什么时候恢复阻塞。而且挂起队列在操作系统里可以看成一个,而阻塞队列则是不同的事件或资源(如信号量)就有自己的队列。

2.阻塞(pend)就是任务释放CPU,其他任务可以运行,一般在等待某种资源或信号量的时候出现。挂起(suspend)不释放CPU,如果任务优先级高就永远轮不到其他任务运行,一般挂起用于程序调试中的条件中断,当出现某个条件的情况下挂起,然后进行单步调试。

3.pend是task主动去等一个事件,或消息.suspend是直接悬挂task,以后这个task和你没任何关系,任何task间的通信或者同步都和这个suspended task没任何关系了,除非你resume task;

4.任务调度是操作系统来实现的,任务调度时,直接忽略挂起状态的任务,但是会顾及处于pend下的任务,当pend下的任务等待的资源就绪后,就可以转为ready了。ready只需要等待CPU时间,当然,任务调度也占用开销,但是不大,可以忽略。可以这样理解,只要是挂起状态,操作系统就不在管理这个任务了。

5.挂起是主动的,一般需要用挂起函数进行操作,若没有resume的动作,则此任务一直不会ready。而阻塞是因为资源被其他任务抢占而处于休眠态。两者的表现方式都是从就绪态里“清掉”,即对应标志位清零,只不过实现方式不一样。

这些术语是针对线程或进程来说的,比如有种叫法“进程三态”什么的,但是不同的操作系统,或者同种操作系统不同版本见也存在状态管理方式上的不同,比如还有什么“进程五态”、“进程七态”的,我们可以把操作系统对进程的控制形象地比作雇人打工。

1、睡眠态:雇主对雇工说:“你睡觉去吧,某时某刻回来报道,接着干活!”

2、挂起态:雇主对雇工说:“你睡觉去吧,用得着你的时候,我会去叫你的,接着干活!”

3、就绪态:雇工的扫帚(指CPU)被别的雇工抢了,自己只能按照规定的方式参与竞争(比如Linux中是按时间片轮转的方式),抢到就是自己的,然后接着干活!

4、阻塞态:雇工的抹布(指非CPU的其他资源)被别的雇工抢了,自己只能干等着,等到了抹布,就进入就绪态,继续参与到抢扫帚的竞赛中。

这里可以想象,睡眠和挂起应该是差不多的,睡眠应该是挂起多长时间后回来,但是他们都是一直占着CPU的。

WINDOWS不是实时操作系统,是抢占式的多线程操作系统,系统可以在任何时候停止一个线程调用另外的线程。


一个线程可以被挂起多次,SuspendThread多少次就要ResumeThread多少次,除了被别人调用SuspendThread挂起外,线程也可以告诉系统在一段时间内,可以将自己挂起,不需要调度。这可以调用Sleep实现。

1 void Sleep(DWORD dwMilliseconds);

这里参数是表示线程要挂起多长时间,但是WINDOWS不是实时操作系统,不能保证到时候准时醒来,当为其传入0时,表示主调线程主动放弃本次时间片的剩余部分。注意是本次。


当计算线程执行某任务所需的时间的时候,很多人习惯使用GetTickCount64

123 ULONG start=GetTickCount64();  //do something.ULONG end=GetTickCount64();

这个函数可能会由于线程中途被挂起而变得不准确,Windows提供了一个函数可以返回一个线程以获得cpu时间

123456 BOOL GetThreadTime(        HANDLEhThread,        PFILETIME pftCreationTime,        PFILETIME pftExitTime,       PFILETIME pftKernelTime,       PFILETIME pftUserTime);

第一个参数为想获得的线程句柄。

第二个参数返回(线程创建时间-1601年1月1日0:00)的秒数。单位是100ns。

第三个表示退出时间-1601年1月1日0:00的秒数。单位是100ns。

第四个表示线程执行内核模式下的时间的绝对值。单位是100ns。

第五个表示线程执行用户模式代码的时间的绝对值。单位是100ns。

类似的,GetProcessTime可以返回进程中所有线程的时间之和。

在进行高精度的计算时上述函数仍然不够。此时windows提供了以下函数:

12 BOOL QueryPerformanceFrequency(LARGE_INTEGER *pliFrequency)BOOL  QueryPerformanceCounter(LARGE_INTEGER *pliCount);

这两个函数假设正在执行的线程不会被抢占。它们都是针对生命期很短的代码块。GetCPUFrequencyInMHZ可以获得cpu频率。