Windows 线程(七) 线程的状态
通过 Spy++ 来观察线程
我们都知道, 我们通常会使用 Spy++ 工具来查找窗口
但是 Spy++ 还可以观察进程和线程
如图: 

我们随便找一个线程, 观察线程的属性: 
我们可以看到, 里面有线程状态, 目前为等待
这就说明我们当前线程的暂停计数不为0
还有一个是上下文开关, 这个是当前线程被加载之后运行的次数
线程的状态
线程的状态:
- 启动
- 运行
- 挂起
- 等待/休眠
- 消亡
线程的启动状态
在线程启动后, 会有一个CONTEXT(线程上下文)
会有使用计数, 值为2
会有暂停计数, 值为1
当CreateThread完成后, dwCreationFlags参数不是CREATE_SUSPENDED, 暂停计数会减一
当暂停计数为0时, 进入CPU调度, 当前线程为可执行状态
线程的运行状态
进入运行状态后, 线程开始执行我们的函数功能
在执行过程中, 线程会时不时的进行切换
- 暂停的时候, 写入CPU状态到CONTEXT中, 也就是保存线程运行上下文
- 线程进行等待
- 然后又被调度到后, 进行读取CONTEXT到CPU, 继续运行
线程运行状态中的线程切换是不会修改暂停计数的
线程的挂起状态
可以通过调用SuspendThreadAPI来挂起线程
注意: 如果是64位的程序, 需要调用Wow64SuspendThread来挂起线程
挂起状态就是线程暂停
它会让我们线程的暂停计数加一
当暂停计数不为0时, CPU将线程从调度池中取出
该线程不会参与任何的CPU调度
CPU也不会管该线程的任何信号状态
#include <Windows.h>
#include <process.h>
#include <tchar.h>
unsigned int __stdcall ThreadMain(void*)
{
INT nNum = 0;
while(TRUE)
{
_tprintf(TEXT("%d\n"), nNum++);
}
return 0;
}
INT main()
{
HANDLE hThread = (HANDLE)_beginthreadex(NULL, 0, ThreadMain, NULL, 0, NULL);
Sleep(100);
SuspendThread(hThread); // 挂起线程, 暂停计数+1
SuspendThread(hThread); // 在挂起一次, 暂停计数依然+1, 下面就需要两次ResumeThread才行
Sleep(1000);
ResumeThread(hThread); // 恢复线程运行, 暂停计数-1
WaitForSingleObject(hThread, INFINITE);
return 0;
}SuspendThread是非实时的, 它只是向操作系统发送请求
它会等到当线程的当前CPU时间片运行完成之后, 由操作系统来进行操作
SuspendThread的返回值是当前线程被挂起的次数, 就是线程的暂停计数-1
注意, 不推荐进程挂起操作, 除非能保证挂起线程没有问题.
举例:
在线程中, 需要进行new操作
当new的空间分配完成, 但是还未标记这块堆空间被使用时, 线程被挂起
那么, 操作系统不会认为这块堆内存被使用, 可能会被其他线程进行使用修改
那么当线程再次恢复运行时, 去读取这个堆内存上的数据就是错误的.
通过SuspendThread的挂起和CPU调度时的挂起不是一回事
线程的等待/休眠状态
通过SleepAPI来使线程进入等待/休眠状态
它不会改变线程的暂停计数
它会通知CPU在指定的毫秒数内不要调度本线程
Sleep的参数中的毫秒数并不是绝对的, 只是无限接近
调用Sleep后, 会进行如下操作:
- 线程将放弃剩余的时间片
通知操作系统不要来调度我, 然后等待指定毫秒数
- 当传递的毫秒数为
INFINITE时, 线程会永远等待 - 一直等到进程消亡
- 当传递的毫秒数为
0时, 直接放弃剩余时间片
- 当传递的毫秒数为
饥饿线程
通过SwitchToThreadAPI可以切换到另一个线程
要切换的线程是不确定的
它会根据操作系统的CPU时间饥饿度算法来切换
当线程调用SwitchToThread后, 会将剩余的时间片给饥饿度最高的线程执行
未完待续...
如有错误,请提出指正!谢谢.
本文由 花心胡萝卜 创作,采用 知识共享署名4.0 国际许可协议进行许可
本站文章除注明转载/出处外,均为本站原创或翻译,转载前请务必署名
最后编辑时间为: 2017-06-21 at 03:19 am