Windows核心编程--线程调度/优先级/关联性

-上下文切换
大约每个20ms,Windows会查看所有当前存在的线程内核对象。在这些对象中,只有一些被认为是可调度的。Windows在可调度的线程内核对象中选择一个,并将上次保存在线程上下文中的值(保存在线程内核对象的CONTEXT结构成员里)载入CPU寄存器。

Windows之所以被称为抢占式多线程操作系统,是因为系统可在任何时刻停止一个线程而另行调度另一个线程。
被挂起的线程,等待某事件发生的线程,没有任务的线程都属于不可调度的。

-线程的挂起和恢复
线程内核对象中有一个值表示线程的挂起计数。
调用CreateProcess或CreateThread时,系统将创建线程内核对象,并把挂起计数初始化为1。线程初始化之后,如CreateProcess或CreateThread传入CREATE_SUSPENDED,维持挂起状态。如没传入,将挂起计数递减为0,线程变成可调度的。

// 成功时,返回线程的前一个挂起计数
// 失败时,返回0xFFFFFFFF
DWORD ResumeThread(HANDLE hThread);

// 挂起,返回线程之前的挂起计数
DWORD SuspendThread(HANDLE hThread);

// 一个线程最多可挂起 MAXIMUM_SUSPEND_COUNT

实际开发中,调用SuspendThread时必须小心,因为,试图挂起一个线程时,不知道线程在做什么。如线程正在分配堆中的内存,线程将锁定堆。当其它线程要访问堆的时候,它们的执行将被中止,直到第一个线程恢复。

-进程的挂起和恢复
Windows中不存在挂起和恢复进程的概念,系统不会给进程调度CPU时间。

// 使用此函数时,应考虑到 目标进程会不会在此函数执行过程中,发生线程的创建,结束等行为
VOID SuspendProcess(DWORD dwProcessID, BOOL fSuspend)
{
    HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, dwProcessID);
    if(hSnapshot != INVALID_HANDLE_VALUE)
    {
        THREADENTRY32 te = { sizeof(te) };
        BOOL fOK = Thread32First(hSnapshot, &te);
        for(; fOK; fOK = Thread32Next(hSnapshot, &te))
        {
            if(te.th32OwnerProcessID == dwProcessID)
            {
                HANDLE hThread = OpenThread(THREAD_SUSPEND_RESUME, FALSE, te.th32ThreadID);
                if(hThread != NULL)
                {
                    if(fSuspend)
                        SuspendThread(hThread);
                    else
                        ResumeThread(hThread);
                }
                CloseHandle(hThread);
            }
        }
        CloseHandle(hSnapshot);
    }
}

HANDLE OpenThread(
DWORD dwDesiredAccess,
BOOL bInheritHandle,
DWORD dwThreadID
);

-睡眠

// 告诉系统,在一段时间内自己不需要调度了
VOID Sleep(DWORD dwMilliseconds);

1.将使线程自愿放弃属于它的时间片中剩下的部分。
2.系统设置线程不可调度的时间只是“近似于”所设定的毫秒数。
3.传入INFINITE,告诉系统永远不要调度此线程。
4.传入0,告诉系统,主调线程放弃了时间片的剩余部分。

-线程执行时间

ULONGLONG qwStartTime = GetTickCount64();
...
// 统计两个时间点的时间差。
// 如中间过程线程被中断,则,非执行时间也计入统计结果。
ULONGLONG qwElapsedTime = GetTickCount64() - qwStartTime;

BOOL GetThreadTimes(
HANDLE hThread,
// 100ns为单位。1601.1.1.子夜
PFILETIME pftCreationTime,
// 100ns为单位。1601.1.1.子夜
PFILETIME pftExitTime,
// 100ns为单位。执行内核代码时间。
PFILETIME pftKernelTime,
// 100ns为单位。执行用户代码时间。
PFILETIME pftUserTime
);

BOOL GetProcessTimes(
HANDLE hProcess,
// 100ns为单位。1601.1.1.子夜
PFILETIME pftCreationTime,
// 100ns为单位。1601.1.1.子夜
PFILETIME pftExitTime,
// 100ns为单位。执行内核代码时间。
PFILETIME pftKernelTime,
// 100ns为单位。执行用户代码时间。
PFILETIME pftUserTime
);

-系统为线程分配CPU时间的几种方式
1.依赖约10-15ms的间隔时钟计时器。
2.依赖处理器的64位时间戳计时器,它计算的是机器启动以来的时钟周期数。

当线程被调度程序暂停时,将计算此时TSC值与线程开始执行时,TSC值的差值,并在线程执行时间上加上这个差值。

// 返回给定线程所用的时钟周期数
ULONGLONG QueryThreadCycleTime(HANDLE hThread);
// 返回给定进程所用的时钟周期数
ULONGLONG QueryProcessCycleTime(HANDLE hProcess);

// 获得当前TSC值
__int64 ReadTimeStampCounter();
依据时钟周期差值来计算时间结果本身无法保证,正确性。因为相同的时钟周期数时,
不同的频率对应不同的时间。而处理器的频率是可能会发生变化的。

// 1秒内的性能计数值数
BOOL QueryPerformanceFrequency(LARGE_INTEGER* pliFrequency);
// 获取当前性能计数值
BOOL QueryPerformanceCounter(LARGE_INTEGER* pliCount);

-在实际上下文中谈CONTEXT
组成部分:
1.CPU的控制寄存器,如指令指针,栈指针,标志,函数返回地址。
2.CPU的整数寄存器。
3.CPU的段寄存器。
4.CPU的调试寄存器。
5.CPU的扩展寄存器。

6.查看线程内核对象内部

// 1.SuspendThread挂起线程。
// 2.获取用户模式线程上下文
BOOL GetThreadContext(
HANDLE hThread,
PCONTEXT pContext
);

// 3.代码设置用户模式线程上下文
BOOL SetThreadContext(
HANDLE hThread,
CONST CONTEXT *pContext
);

一个线程实际有两个上下文:用户模式和内核模式。

-线程优先级
1.调度时,选择 优先级高的可调度线程调度。
2.任何时刻,系统中大多数线程都是不可调度的。
如线程调用GetMessage,而系统并没有消息等待处理,则,暂停此线程,取消这个线程当前时间片的剩余时间,再次进入调度。
3.较高优先级的线程总是会抢占较低优先级的线程。
如较低优先级线程1正在执行,此时,较高优先级线程2变成可调度的,系统立即暂停线程1,将CPU分配给线程2。线程2获得一个完整时间片。
4.系统启动时,创建一个页面清零线程。优先级为0。

-进程优先级类
1.
real-time
REALTIME_PRIORITY_CLASS 进程须具备:Increase Scheduling Priority特权。
2.
high
HIGH_PRIORITY_CLASS 例:任务管理器
3.
above normal
ABOVE_NORMAL_PRIORITY_CLASS
4.
normal
NORMAL_PRIORITY_CLASS
5.
below normal
BELOW_NORMAL_PRIORITY_CLASS
6.
idle
IDLE_PRIORITY_CLASS 例:屏幕保护,后台处理

7.指定
7.1.可在CreateProcess的fdwCreate中指定。
7.2.

BOOL SetPriorityClass(
HANDLE hProcess,
DWORD fdwPriority
);

// 获得进程优先级
DWORD GetPriorityClass(HANDLE hProcess);

-相对线程优先级
1.
time-critical
THREAD_PRIORITY_TIME_CRITICAL
2.
highest
THREAD_PRIORITY_HIGHEST
3.
above normal
THREAD_PRIORITY_ABOVE_NORMAL
4.
normal
THREAD_PRIORITY_NORMAL
5.
below normal
THREAD_PRIORITY_BELOW_NORMAL
6.
lowest
THREAD_PRIORITY_LOWEST
7.
idle
THREAD_PRIORITY_IDLE

进程优先级类+相对线程优先级—》优先级值。

// 设置线程的相对优先级
// 改变优先级前,先挂起线程,设置后,再恢复
BOOL SetThreadPriority(
HANDLE hThread,
int nPriority
);

// 获取线程的相对优先级
int GetThreadPriority(
HANDLE hThread
);

-动态提升线程优先级
线程优先级类+线程相对优先级–》线程优先级值:基本优先级值。
线程的当前优先级不会低于线程的基本优先级。
系统只提升优先级值在1-15的线程,且,系统不会把线程的优先级提升到实时范围。

// 禁止动态提升线程优先级
// 一个进程内所有线程
BOOL SetProcessPriorityBoost(
HANDLE hProcess,
BOOL bDisablePriorityBoost
);

// 一个特定线程
BOOL SetThreadPriorityBoost(
HANDLE hThread,
BOOL bDisablePriorityBoost
);

BOOL GetProcessPriorityBoost(
HANDLE hProcess,
PBOOL pbDisablePriorityBoost
);

BOOL GetThreadPriorityBoost(
HANDLE hThread,
PBOOL pbDisablePriorityBoost
);

-为前台进程微调调度程序
如用户需使用某进程的窗口,这个进程称为前台进程。
为改进前台进程的响应性,windows会为前台进程中的线程微调调度算法。(微调只在前台进程优先级类为normal时,进行。)

-调度I/O请求优先级
1.线程会产生I/O请求。
2.低优先级线程产生大量I/O请求,在I/O请求未完成时,可能会挂起高优先级线程。
3.现在,线程可以在进行I/O请求时设置优先级了。

// SetThreadPriority传入THREAD_MODE_BACKGROUND_BEGIN,告诉Windows,线程应发送低优先级的I/O请求。
// SetThreadPriority传入THREAD_MODE_BACKGROUND_END,让线程进行normal优先级的I/O请求。
// 设置I/O优先级时,只允许线程设置自己的。所以,传入的必须是主调线程的句柄。

// 对进程中所有线程使用
// SetPriorityClass传入PROCESS_MODE_BACKGROUND_BEGIN
// SetPriorityClass传入PROCESS_MODE_BACKGROUND_END。
// 设置进程I/O优先级时,也只允许进程设置自己的。所以,传入的必须是主调进程的句柄。

// 对某个文件指定优先级。此时,对此文件,以这里指定的为准。
FILE_IO_PRIORITY_HINT_INFO phi;
phi.PriorityHint = IoPriorityHintLow;
SetFileInformationByHandle(
hFile,
FileIoPriorityHintInfo,
&phi,
sizeof(PriorityHint)
);

-关联性
1.软关联
给线程分配处理器时,如果其它因素一样,系统将使线程在上次运行的处理器上运行。

2.NUMA:非同一内存访问的计算机体系结构
结构的计算机由多个系统板组成,每个系统板都有自己的CPU和内存板块。

此体系结构下,系统在CPU只访问自己所在的系统板的内存时,可达到最佳性能。
为支持这种体系结构,允许我们设置进程和线程的关联性。
硬关联。

// 1.调用GetSystemInfo来查询机器上CPU的数量。
// 2.设置进程中线程可使用的CPU子集
BOOL SetProcessAffinityMask(
HANDLE hProcess,
DWORD_PTR dwProcessAffinityMask
);

DWORD_PTR SetThreadAffinityMask(
HANDLE hThread,
// 须是所在进程的关联性掩码的真子集
DWORD_PTR dwThreadAffinityMask
);

// 子进程将继承进程的关联性
// 也设置作业对象的关联性

// 返回关联性
BOOL GetProcessAffinityMask(
HANDLE hProcess,
PDWORD_PTR pdwProcessAffinityMask,
PDWORD_PTR pdwSystemAffinityMask
);

// 指定一个理想型CPU。索引,非掩码。
DWORD SetThreadIdealProcessor(
HANDLE hThread,
DWORD dwIdealProcessor
);
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

raindayinrain

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值