第7章 线程调度、优先级和亲缘性

7.1 线程的挂起和恢复

(1)线程挂起

  ①创建时(如CreateProcess、CreateThread),传入CREATE_SUSPENDED标志

  ②用SuspendThread挂起线程。这个函数可以挂起自己,也可以挂起其它线程(只要有线程句柄)

  ③线程在挂机计数不为0或没有消息队列没有消息时,是不可调度的(没时间片)

(2)线程恢复:ResumeThread,返回前一个挂起计数,否则返回0xFFFFFFFF。

  ①一个线程可以被多次挂起,最多可以挂起MAXIMUNM_SUSPEND_COUNT(127)次

  ②线程被挂起多次时,只有恢复到挂起计数为0时,才可以重新被调度。

  ③调用SuspendThread时,如果这时线程执行在用户态,线程会马上被挂起。如果调用SuspendThread时线程己经执行在内核态时,SuspendThread会异步返回,而线程并不会马上暂停。但当该线程从内核态又转为用户态时,则会立即被暂停。

  ④当调用SuspendThread挂起线程时,我们并不知道线程在做什么?如果此时A线程正在分配堆中的内存,则它将会锁定堆,这会导致此时也要访问堆的B线程被中止,直到A恢复,而这可能引起其他问题或者死锁。所以只有在确切目标线程在哪里(或在做什么时)调用SuspendThread才是安全的。

(3)可以调用GetSystemTimeAdjustment来查看线程切换的周期(大约20ms)

7.2 进程的挂起和恢复

(1)进程的挂起——挂起进程中所有的线程(注意进程本身是不可调度的)

  ①Windows没有提供挂起进程中所有线程的方法,因为存在竞态条件问题。(如在线程被挂起时,可能创建一个新的线程。系统必须想方设法挂起这个时间段内创建任何新线程。

  ②自定义的SuspendProcess函数(用来挂起所有线程)

【SuspendProcess程序】

#include <windows.h>
#include <stdio.h>
#include <Tlhelp32.h>
#include <tchar.h>
#include <locale.h>

//挂起进程中所有的线程
void SuspendProcess(DWORD dwProcessID, BOOL fSuspend)
{
    DWORD dwThreadID = GetCurrentThreadId(); //主调线程ID

    //获取系统中所有线程列表,第2个参数为0时表示当前进程
    HANDLE hSnapshot = CreateToolhelp32Snapshot
                           (TH32CS_SNAPTHREAD, dwProcessID);
    if (hSnapshot != INVALID_HANDLE_VALUE){
        /*          以下在枚举线程过程中,可能有新的线程创建,也可能有线程被销毁。但CreateToolhelp32SnapShot只是快照,无法反应这一变化。
          所以新的线程就不会被挂机。同时,被销毁的线程ID可能被另一个进程中的线程给占用,这会造成误挂其他进程中的线程的潜在风险。因此这个函数要慎用。
        */
        //遍历线程列表
        THREADENTRY32 te = { sizeof(te) };
        BOOL fOk = Thread32First(hSnapshot, &te);
        for (; fOk;fOk=Thread32Next(hSnapshot,&te)){

            //线程是否在目标进程中
            if (te.th32OwnerProcessID != dwProcessID)
                continue;;

            //尝试将线程ID转换为线程句柄(可挂机和恢复线程)
            HANDLE hThread = OpenThread(THREAD_SUSPEND_RESUME,
                                        FALSE, te.th32ThreadID);
            if (hThread != NULL){
                //挂机或恢复线程
                if (fSuspend)
                {
                    //防止主调函数挂起自己,导致循环无法进行而不能挂机其他线程
                    if (te.th32ThreadID != dwThreadID)
                        SuspendThread(hThread);
                } else
                    ResumeThread(hThread);
            }
            CloseHandle(hThread);
        }

        CloseHandle(hSnapshot);

        //如果要挂机进程,最后挂起主调进程
        if (fSuspend){
            HANDLE hThread = OpenThread(THREAD_SUSPEND_RESUME,
                                        FALSE, dwThreadID);
            if(hThread)
                SuspendThread(hThread);
            CloseHandle(hThread);
        }
    }
}

//线程函数
DWORD WINAPI ThreadProc(PVOID pParam)
{
    while (TRUE){
        _tprintf(_T("线程(ID:0x%04X),正在输出... \t时间%d\n"),
                 GetCurrentThreadId(), GetTickCount());
        Sleep(1000);
    }
    return 0;
}

int _tmain()
{
    _tsetlocale(LC_ALL, _T("chs"));

    //创建两个线程,线程刚创建时是挂起的——这两个线程用来测试SuspendProcess函数
    HANDLE hThread1 = CreateThread(NULL, 0, ThreadProc, NULL, CREATE_SUSPENDED, NULL);
    HANDLE hThread2 = CreateThread(NULL, 0, ThreadProc, NULL, CREATE_SUSPENDED, NULL);

    CloseHandle(hThread1);
    CloseHandle(hThread2);

    //唤醒所有线程
    _tprintf(_T("正在唤醒线程...\n"));
    SuspendProcess(GetCurrentProcessId(), FALSE);

    _tsystem(_T("PAUSE"));

    //在线程运行过程, 按任意键可以挂起进程
    _tprintf(_T("进程己被挂起!\n"));
    SuspendProcess(GetCurrentProcessId(), TRUE);

    //因所有线程都被挂起,进程中无活动线程,故进程(线)程无法被唤醒
    //所以后面的代码无法执行!
    //再次唤醒所有线程
    _tprintf(_T("正在唤醒线程...\n"));
    SuspendProcess(GetCurrentProcessId(), FALSE);
    _tsystem(_T("PAUSE"));
    return 0;
}
时间: 2024-10-12 11:01:32

第7章 线程调度、优先级和亲缘性的相关文章

Windows内核之线程的调度,优先级,亲缘性

1 调度 Windows不是实时操作系统,它是抢占式多线程操作系统.在假设所有优先级相同的情况下,CPU对线程的调度原则是每隔20m就会切换到下一个线程,根据Context中的IP和SP来接着执行上次的东西.Windows永远不会让1个线程去独占一段时间. 2 可调度性 系统只调用可以调度的线程,其实系统的大部分线程都是处于不可调度的状态,要么处于暂停的状态,要么处于休眠的状态. 3 线程的暂停和恢复 <1>在CreateThread的时候通过制定CREATE_SUSPENDED来让线程暂停执

回炉重造之重读Windows核心编程-007-线程的调度、优先级与亲缘性

Windows被设计成一个抢占式的操作系统,用某种算法来确定哪些线程应该在何时被调度和运行多长时间.每隔20ms左右,Windows就要查看当前所有线程的内核对象,找到可以被调度的一个,将它加载到CPU寄存器中.这个操作成为上下文切换.Windows实际上保存了一个记录,说明每个线程获得了多少次运行的机会.使用MicrosoftSpy++这个工具可以了解这个情况. 一个线程随时可以停止运行,一个线程可以进行调度.可以对线程进行一定程度的控制,但是不能太多.不能保证一个线程做任何事. 7.1暂停和

第7章 线程调度、优先级和亲缘性(2)

7.7 在实际上下文中谈CONTEXT结构 (1)线程CONTEXT记录线程的状态(如CPU各寄存器状态),以供下次调度时从停止处继续. (2)CONTEXT的结构(要获得或设置时,必须在Context.ContextFlags设置相应的标志) 标志 说明 CONTEXT_CONTROL 控制寄存器,如EIP.ESP,EBP等 CONTEXT_INTEGER 整数寄存器,如EDI.ESI.EBX.EDX.ECX.EAX等 CONTEXT_FLOATING_POINT 浮点寄存器,将寄存器结果返回

Linux CPU亲缘性详解

前言 在淘宝开源自己基于nginx打造的tegine服务器的时候,有这么一项特性引起了笔者的兴趣.“自动根据CPU数目设置进程个数和绑定CPU亲缘性”.当时笔者对CPU亲缘性没有任何概念,当时作者只是下意识的打开了google并输入CPU亲缘性(CPU Affinity)简单了做了个了解. 后来,在笔者参加实际工作以后,就碰到了这么两个问题. 问题一:如何在SMP的系统中,保证某个特定进程即使在其他进程都很忙的情况下都能够获得足够的CPU资源?解决的思路主要有以下两种: 提高进程的处理优先级 从

SetThreadAffinityMask设置线程亲缘性

The SetThreadAffinityMask function sets a processor affinity mask for the specified thread. [delphi] view plaincopyprint? DWORD_PTR SetThreadAffinityMask( HANDLE hThread, DWORD_PTR dwThreadAffinityMask ); Parameters hThread [in] Handle to the thread

设置线程的亲缘性(指定其所运行的CPU核心)

#include <stdio.h> #include <windows.h> #include <process.h> #include <time.h> unsigned int _stdcall thread_proc(void* arg) { double x = 100.0; int r = 0; srand((unsigned)time(0) ); while(1) { r = rand();//模拟运算 x/=r; } return 0; }

进程、线程亲缘性和画笔CPen

l         进程亲缘性 l         线程亲缘性 l         画笔CPen   一.进程线程亲缘性  SetProcessAffinityMask  //设置进程 可以使用的CPU SetThreadAffinityMask  //设置进程 可以使用的CPU void CDialog_Thread_Priority_test::OnBnClickedButton5() { // TODO: 在此添加控件通知处理程序代码 HANDLE ph=GetCurrentProcess

【记录一个问题】android ndk下设置线程的亲缘性,总有两个核无法设置成功

参考了这篇文章:https://blog.csdn.net/lanyzh0909/article/details/50404664 大体的代码如下: #include <pthread.h> #include <sched.h> #include <stdio.h> #include <stdlib.h> #include <sys/sysinfo.h> #include <sys/types.h> #include <unis

1.10.2线程的优先级的规则性

测试 package com.cky.prioritydemo; import java.util.Random; /** * Created by edison on 2017/12/3. */ public class MyThread1 extends Thread{ @Override public void run() { super.run(); long begin = System.currentTimeMillis(); long addResult =0 ; for (int