每个现场都有一个上下文。后者保存在线程的内核对象中。这上下文反应了线程上一次执行时CPU寄存器的状态,大约每隔20ms(GetSystemTimeAdjustment函数第二个参数的返回值),windows都会查看所有当前存在的线程内核对象,在这些对象中,只有一些被认为是可以调度的,windows在可调度的线程内核对象中选择一个,并将上次保存在线程上下文中的值载入CPU寄存器,这一操作被称为上下文切换。

    在县城初始化之后,CreateProcess或者CreateThread函数将查看是否有CREATE_SUSPENDED标志传入,如果有,函数会返回并让新线程处于挂起状态。如果没有函数将线程的挂起计数递减为0,当线程的挂起计数为0时,线程就成为可调度的,除非还在等待某个事件 的发生。通过创建一个处于挂起状态的线程,我们可以通过ResumeThread来恢复线程。同样也有SuspendThread可以挂起线程。

    线程可以告诉系统要休眠,调用Sleep函数

    1.调用Sleep函数将使得线程资源放弃属于他的时间片中剩下的部分

    2.系统设置线程不可调度的时间只是近似于所设定的毫秒数,windows不是实时操作系统,线程实际醒来的情况取决于系统中其他线程的运行情况

    3.可以调用Sleep函数并给dwms参数传入INFINITE,这是在告诉系统,永远不要调度这进程

    4.Sleep(0),这是在告诉系统,主调线程放弃了时间片的剩余部分,它强制系统调度其他线程,但是系统有可能重新调低刚刚调用了sleep(0)的线程,如果没有相同或者较高优先级的可调度线程时,就会发生这样的情况。

    切换到另一个线程

    系统提供了一个名为SwitchToThread的函数,如果存在另一个可调度的线程,那么系统会让此线程运行。

    Bool SwitchToThread();

    调用这函数时,系统查看是否存在正急需CPU时间的饥饿线程,如果没有,函数立即返回,如果存在,则调度该线程(其优先级可能比SwitchToThread的主调线程低),饥饿线程运行一个时间量,然后系统调度程序回复正常运行。

    通过调用SwitcgToThread和调用Sleep(0)类似,区别在于SwitchToThread运行执行较低优先级的线程。

    线程的执行时间

    有时候我们需要计算一个线程执行某项任务需要消耗多长时间,关于这一点,可以利用GetTickCount64();

    但是有一个前提就是代码的执行不会被中断,可是在抢占式的操作系统,这样是比较困难的。

    1. BOOL WINAPI GetThreadTimes(
    2. _In_ HANDLE hThread,//需要获取cpu时间的线程句柄
    3. _Out_ LPFILETIME lpCreationTime,//创建时间
    4. _Out_ LPFILETIME lpExitTime,//退出时间
    5. _Out_ LPFILETIME lpKernelTime,//内核时间
    6. _Out_ LPFILETIME lpUserTime//用户时间
    7. );

    同样的也有GetProcessTimes,可以用于进程中的所有线程。

    1. BOOL WINAPI GetProcessTimes(
    2. __in HANDLE hProcess,
    3. __out LPFILETIME lpCreationTime,
    4. __out LPFILETIME lpExitTime,
    5. __out LPFILETIME lpKernelTime,
    6. __out LPFILETIME lpUserTime
    7. );

    hProcess:为需要获取相关时间的进程句柄
    lpCreationTime:进程的创建时间
    lpExitTime:进程的退出时间
    lpKernelTime:进程在内核模式下的所有时间
    lpUserTime:进程在用户模式下的所有时间

    1. BOOL QueryPerformanceFrequency(LARGE_INTEGER *pliFrequency)
    2. BOOL QueryPerformanceCounter(LARGE_INTEGER *pliCount);

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

    在windows定义的所有数据结构中,CONTEXT结构是唯一一个依赖于cpu的。我们可以通过调用GetThreadContext来获得当期cpu寄存器的状态。

    1. BOOL GetThreadContext(HANDLE hThread, LPCONTEXT lpContext);

    hThread:获取信息目标进程的线程句柄。(用OpenThread获取)
    lpContext:一个用于接收信息的CONTEXT结构指针
    返回值:如果成功,返回值不为零,如果不成功,返回值为零,若想看其他的错误信息,参考GetLastError

    第二个参数是CONTEXT结构指针。在分配CONTEXT结构后,需要初始化ContextFlag标志,表示以表示要获取哪些寄存器。函数执行后CONTEXT对象中就填入我们请求的成员。

    ContextFlag可以是:

    CONTEXT_CONTROL表示控制寄存器。
    CONTEXT_INTEGER表示整数寄存器。
    CONTEXT_FLOAT 表示浮点寄存器。
    CONTEXT_ALL 表示CONTEXT_CONTROL |CONTEXT_INTEGER|CONTEXT_SEGMENTS。
    在调用GetThreadContext时,需要先调用SuspendThread。因为在调用GetThreadContext时系统可能正在执行那个线程,此时线程的上下文与获得的信息就不一致了。注意,它只能返回线程的用户模式上下文。如果当调用SuspendThread时线程正在内核模式运行,线程不会暂停,直到其返回用户空间。但是返回到用户控件后不会执行任何用户模式代码。

    不仅仅能获得线程的进程上下文,我们还可以设置它。这可以调用:

    1. BOOL SetThreadContext( HANDLE hThread,CONST CONTEXT *pContext);

    GetThreadContext和SetThreadContext函数为我们提供了对线程许多控制的方法,但是需要小心使用。

    线程优先级

    前面提到的调度程序在调度另外一个线程之前,可以运行一个线程大约20ms的时间。但是这是所有优先级都相同的情况。实际上系统中的很多线程优先级是不同的,这将影响调度程序如何选择下一个要运行的线程。

    Windows的线程优先级从0到31。每个线程都会分配一个优先级。当系统确定给哪个线程分配cpu时,它会首先查看优先级为31的线程,直至所有优先级为31的线程都被调度。然后再查看下一优先级线程。只要存在优先级为31的线程,系统就不会调度0-30级的线程。低优先级线程长时间得不到cpu时间,这被称为饥饿。这不经常出现,因为大多数线程都是不可调度的。

    系统启动时会创建一个优先级为0的idle线程,整个系统只有它的优先级为0。它在系统中没有其他线程运行时将系统内存中所有闲置页面清0。

    Windows中的线程优先级是由优先级类和相对线程优先级来确定的。系统通过线程的相对优先级加上线程所属进程的优先级来确定线程的优先级值。这个值被称为线程的基本优先级值。

    Windows支持6个进程优先级类:idle ,below normal ,normal ,above normal,high和real-time。它们是相对与进程的。Normal最为常用,为99%的进程使用。

    idle优先级类在系统什么都不做的时候运行的应用程序。如屏幕保护程序。real-time优先级类优先级别最高,但是没有开放给用户使用。因为此优先级类的程序会影响操作系统的任务。

    Windows支持7个相对线程优先级:idle,lowest ,below normal,normal,above normal,highest和time-critical。这些优先级是相对于进程优先级的。大多数的线程使用normal优先级。

    概括起来就是进程属于某个优先级类,另外还可以指定进程中线程的相对线程优先级。也就是说线程优先级是相对于进程优先级的。time-critical优先级对于real-time优先级类,优先级为31。相对于其他优先级类则为15。需要注意的是进程优先级是抽象的概念,因为进程并不参与调度。
    在优先级编程时,首先需要在调用CreateProcess时可以再fdwCreate参数中传入想要的优先级。fdwCreate可以是以下标识符:

    1. real-time REALTIME_PRIORITY_CLASS
    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

    进程运行后可以调用SetPrioritClass来改变进程优先级类。

    1. BOOL SetPriorityClass(HANDLE hProcess,DWORD fdwPriority);

    可以调用GetPriorityClass来获得进程的优先级类。DWORD GetPriorityClass(HANDLE hProcess);
    上面是指定的进程优先级类,调用CreateThread创建线程时,它的线程优先级总是被设置为normal.

    关联性
    默认情况下,windows在分配cpu时采用软关联的方式。也就是说在其他因素相同的情况下,系统使线程在上一次运行的处理器上运行。这有助于重用仍在处理器高速缓存中的数据。
    系统在启动时确定cpu数量。应用程序可以通过调用GetSysInfo来查询cpu的数量。如果需要限制一个进程的所有线程在某些cpu上运行,可以调用:

    BOOL SetProcessAffinityMask( HANDLE hProcess,DWORD_PTR dwProcessAffinityMask);
    第一个参数代表要设置的进程句柄。
    第二参数是一个位掩码。代表线程可以在哪些cpu上运行。
    注意子进程将继承父进程的关联性。
    GetProcessAffinityMask返回进程的关联掩码。
    相应的还可以设置某个线程只在一组cpu上运行:
    SetThreadAffinityMask。
    有时候强制一个线程只在某个特定的cpu上运行并不是什么好主意。Windows允许一个线程运行在一个cpu上,但如果需要,它将被移动到一个空闲的cpu上。
    要给线程设置一个理想的cpu,可以调用:

    1. DWORD SetThreadIdealProcessro(HANDLE hThreadDWORD dwIdealProcessor);

    dwIdealProcessor是一个0到31/63之间的整数。表示线程希望设置的cpu。可以传入MAXIMUM_PROCESSOR值,表示没有理想的cpu。
    ————————————————
    版权声明:本文为CSDN博主「swartz_lubel」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
    原文链接:https://blog.csdn.net/swartz_lubel/article/details/60781616