第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

浮点寄存器,将寄存器结果返回到FLOATING_SAVE_AREA FloagSave

CONTEXT_SEGMENTS

段寄存器,如GS、FS、ES、DS

CONTEXT_DEBUG_REGISTERS

调试寄存器,如DR0、……、DR7

CONTEXT_EXTENDED_REGISTERS

扩展寄存器,将寄存器的结果返回到

BYTE ExtendedRegisters[MAXIMUM_SUPPORTED_EXTENSION]数组中

CONTEXT_FULL标志 = CONTEXT_CONTROL | CONTEXT_INTEGER | CONTEXT_SEGMENTS

(3)获取和设置上下文

①先挂起线程设置CONTEXT结构体相应的标志

②GetSetThreadContext;

【ThreadContext程序】显示线程上下文的CPU寄存器状态

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

//线程函数
DWORD WINAPI ThreadProc(PVOID pvParam)
{
    HANDLE hEvent = (HANDLE)pvParam;
    WaitForSingleObject(hEvent, INFINITE);
    CloseHandle(hEvent);
    return 0;
}

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

    HANDLE hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
    HANDLE hEventDup = NULL;
    DuplicateHandle(GetCurrentProcess(), hEvent, 
                    GetCurrentProcess(), &hEventDup,
                    DUPLICATE_SAME_ACCESS,FALSE,0);
    
    HANDLE hThread = CreateThread(NULL, 0, ThreadProc, hEventDup,
                                  CREATE_SUSPENDED,NULL);
    ResumeThread(hThread);

    SuspendThread(hThread);
    CONTEXT ct = {0};
    ct.ContextFlags = CONTEXT_ALL;
    GetThreadContext(hThread, &ct);

    //显示CONTEXT的内容
    _tprintf(_T("CPU寄存器状态:
"));
    _tprintf(_T("	EAX=0x%08X,EBX=0x%08X
"),ct.Eax,ct.Ebx);
    _tprintf(_T("	ECX=0x%08X,EDX=0x%08X
"), ct.Ecx, ct.Edx);
    _tprintf(_T("	ESI=0x%08X,EDI=0x%08X
"), ct.Esi, ct.Edi);
    _tprintf(_T("	EIP=0x%08X,ESP=0x%08X
"), ct.Eip, ct.Esp);
    _tprintf(_T("	EBP=0x%08X,EFL=0x%08X
"), ct.Ebp, ct.EFlags);

    ResumeThread(hThread);
    SetEvent(hEvent);

    CloseHandle(hEvent);
    CloseHandle(hThread);
    _tsystem(_T("PAUSE"));
    return 0;
}

7.8 线程的优先级

(1)线程优先级被分为0-31级,其中0级最低、31级最高。但编程时不能直接更改这个数字,要通过“进程优先级类”和“线程相对优先级”设置。

(2)每次调度时,如果有优先级31级线程可供调度,那系统将CPU分配给该线程。结束后,会查看是否还在在优先级31级的线程可调度,如果存在,它将获得CPU,其他的0-30级是不会分配CPU。这种现象称为“饥饿”

(3)较高优先高级的线程总是抢占较低优先级的,无论较低优先级的线程是否正在执行,如果正在运行,则会被立即暂停,将将CPU分配给较高优先级线程,而且该线程将获得一个完整的时间片

(4)优先级0的线程是个特殊线程,整个系统中只有一个0等级的线程,称为“页面清零线程”,这个线程负责在没有其他线程需要执行的时候,将系统内存中所有闲置页面清零。

7.9 从抽象角度看优先级

(1)进程优先级类

优先级(及标识符)

描述

Time-critical

(REALTIME_PRIORITY_CLASS)

此进程中的线程必须立即响应,执行实时任务。此进程中的线程学会抢占操作系统的组件的CPU时间,使用该优先级类需极为小心。默认下用户进程是不能运行在该优先级类的,除非用户有InCreateSchedulingPriority(提高计划优先级)的特权。

High(高)

(HIGH_PRIORITY_CLASS)

此进程中的线程必须立即响应,执行实时任务。任务管理器运行在这一级。因此用户可通过它来结束失控的进程

Above normal(高于标准)

(ABOVE_NORMAL_PRIOORITY_CLASS)

此进程中的线程运行在Normal和Hight优先级类之间

Normal(标准)

(NORMAL_PRIORITY_CLASS)

此进程中的线程无需特殊调度

Below normal(低于标准)

(BELOW_NORMAL_PRIORITY_CLASS)

此进程中的线程运行在Normal和Idle类之间

Idle(空闲)

(IDLE_PRIORITY_CLASS)

在系统空闲时运行。如屏幕保护程序、后台实用程序和统计数据收集软件通常使用该优先级类

(2)线程的相对优先级

优先级

描述

Time0(实时)

对于进程是real-time优先级类,线程运行在31上;所有其他基本优先级类的进程时运行在15

Highest

线程运行在高于normal之上两个级别

Above normal(高于标准)

线程运行在高于normal之上一个级别

Normal(标准)

运行在normal级别

Below normal(低于标准)

线程运行在低于normal一个级别

Lowest

线程运行在低于normal两个级别

Idle(空闲)

对于real-time优先级类时,线程运行在16,其他优先级类运行在1

(3)线程优先级=进程优先级类和线程的相对优先级映射到0-31级的某个优先级上

线程相对优先级

(及标识符)

进程优先级类

Idle

Below

normal

Normal

Above

 normal

high

Real-time

Time-critial

(THREAD_PRIORITY_TIME_CRITICAL)

15

15

15

15

15

31

Highest

(THREAD_PRIORITY_HIGHEST)

6

8

10

12

15

26

Above normal

(THREAD_PRIORITY_ABOVE_NORMAL)

5

7

9

11

14

25

Normal

(THREAD_PRIORITY_NORMAL)

4

6

8

10

13

24

Below normal

(THREAD_PRIORITY_BELOW_NORMAL)

3

5

7

9

12

23

Lowest

(THREAD_PRIORITY_LOWEST)

2

4

6

8

11

22

Idle

(THREAD_PRIORITY_IDLE)

1

1

1

1

1

16

   说明:

  ①大多数程序线程的优先级为8,即进程优先级类为Normal、线程相对优先级为Normal

  ②线程优先级是相对于进程优先极的,即改变了进程优先级,级程优先权一般也将变化。

  ③注意:表中线程优先级值没有0,因为0优先级保留给页面清零线程

  ④进程为real-time优先级类中的线程,共优先级不低于16。同理,非real-time的线程优先级值不高能高于15。

7.10 优先级编程

(1)获取和设置进程优先级类

  ①在CreateProcess中的fdwCreate参数中传入“进程优先级类”中相应的标志

  ②调用SetPriorityClass函数设置——(可能需要足够的访问权限,因为这函数可以改变系统中任何进程的优先级)

  ③GetPriorityClass获取进程优先级类

  ④可通过命令行(如C:Start /low calc.exe以“低优先级”)来运行就用程序或通过“任务管理器”来改变进程的优先级类

(2)获取和改变线程相对优先级

  ①在CreateThread中传为CREATE_SUSPENDED,然后调用SetThreadPriority。

  ②运行中的线程也可通过SetThreadPriority设置相对优先级,将返回前一个优先级。

  ③GetThreadPriority获取线程相对优先级,但Windows并没有返回线程绝对优先级(即0-31级)的函数。

(3)动态提升线程优先级:

  ①线程优先级的分类:16~31为实时类型,1~15为动态类型,0为系统类型

  ②线程的基本优先级=线程相对优先级与进程优先级类映射出来的值(介于0-31)

  ③有时为及时响应某种事件系统会动态提升线程优先级,如原来的基本优先级为13,会临时提升级别(如提升2),也就是线程当前优先级达到了15。也可能是某个低优先级的线程长时间处于饥饿状态(如3~4秒),系统会临时将优先级提到15。过两个时间片后,优先级将恢复到原来的基本优先级。(但注意,线程当前优先级不会低于基本优先级)

  ④系统只提升动类型(即优先级值为1~15)的线程,这个范围被称为动态优先级范围

  ⑤可通过SetProcessPriorityBoost或SetThreadPriorityBoost来允许或禁止系统动态提升优先级的做法。当然也可以用GetProcessThreadPriorityBoost来查看是否启动这种行为。

(4)为前台进程微调调度程序

  ①前台进程:即用户正在使用进程的某个窗口,该进程为前台进程。其余为后台进程。

  ②系统为前台进程的线程微调调度算法,即比后台进程分配更多的时间片。这种微调只在前台进程是Normal优先级类时才进程,其他优先级类时不进行微调。

  ③可在Windows的“系统属性”对话框→“高级”→“性能”中单击“设置”,在弹出的“性能选项”对话框的“高级”选项卡中的“调整以优先性能中”选择“程序”,如果选择“后台服务”性能,则不会微调。

(5)调度I/O请求优先级

  ①I/O请求会使得CPU将时间片分配给这种I/O处理,而当一个低优先级的线程获得CPU时,它可以在短时间内产生成千个I/O请求。从而抢得CPU,使高优先级的线程被挂起。

  ②在WindowVista开始,线程也可以对I/O请求设置优先级。

  ③通过将线程相对优先极传入THREAD_MODE_BACKGROUND_BEGIN让线程进入后台工作模式此时将只允许发送低优先级的I/O请求,但这同时也降低了线程的CPU调度优先级。要结束后台工作模式里,可以现传入THREAD_MODE_BACKGROUND_END。(但注意,使用这两个标志时,只能传入主调线程的句柄,即GetCurrentThread()。系统不允许改变另一个线程的I/O优先级。

  ④要改变所有线程I/O请求优先级,可用SetPriorityClass并传入PROCESS_MODE_BACKGROUND_BEGIN和PROCESS_MODE_BACKGROUND_END标志。类似地,系统不允许改变另一个进程中线程的I/O优先级。

  ⑤为了避免优先级逆转,如果某Normal优先级线程频繁请求I/O操作时,后台优先级线程可以在获得I/O请求前延迟几秒。如果低优先级的线程己经获得了Normal线程要等待的锁,Normal线程可以主动结束等待,但这影响了Normal线程任务的执行。为了防止这类事情发生,甚至应让后台线程不提交I/O请求,以便Normal线程正常执行,但这几乎很不现实。所以应尽量少在Normal线程与台线程之间同步锁。

【Scheduling Lab示例程序】

 

/************************************************************************/
/* Module:SchdLab.cpp                                                   */
/* Notices:Copyright(c) 2008 Jeffrey Richter & Christophe Nasarre       */
/************************************************************************/

#include "..\..\CommonFiles\CmnHdr.h"
#include <windows.h>
#include <tchar.h>
#include <strsafe.h>
#include "resource.h"

//////////////////////////////////////////////////////////////////////////
DWORD WINAPI ThreadFunc(PVOID pvParam)
{
    HANDLE hThreadPrimary = (HANDLE)pvParam;

    //挂起主线程
    SuspendThread(hThreadPrimary);
    chMB(
        "主线程被挂起,将不再响应输入和产生输出.
"
        "按“确定”按钮恢复主线程,并退出子线程。
"
        );
    ResumeThread(hThreadPrimary);
    CloseHandle(hThreadPrimary);

    //启用“挂机按钮”
    EnableWindow(
         GetDlgItem(FindWindow(NULL, TEXT("Scheduling Lab")), IDC_SUSPEND),
         TRUE);

    return 0;
}

//////////////////////////////////////////////////////////////////////////
BOOL Dlg_OnInitDialog(HWND hwnd, HWND hwndFocus, LPARAM lParam)
{
    chSETDLGICONS(hwnd, IDI_SCHEDLAB);

    //初始化进程优先级类组合框
    HWND hWndCtrl = GetDlgItem(hwnd, IDC_PROCESSPRIORITYCLASS);
    
    int n = ComboBox_AddString(hWndCtrl, TEXT(""));
    ComboBox_SetItemData(hWndCtrl, n, HIGH_PRIORITY_CLASS);

    //保存当前进程优先级类
    DWORD dwpc = GetPriorityClass(GetCurrentProcess());

    //判断系统是否支持“低于标准”优先级类
    if (SetPriorityClass(GetCurrentProcess(),BELOW_NORMAL_PRIORITY_CLASS)){
        
        //恢复原始的进程优先级类
        SetPriorityClass(GetCurrentProcess(), dwpc);

        //增加“高于标准”优先级类
        n = ComboBox_AddString(hWndCtrl, TEXT("高于标准"));
        ComboBox_SetItemData(hWndCtrl, n, ABOVE_NORMAL_PRIORITY_CLASS);
        dwpc = 0; //作为系统是否支持“低于标准”优先级类的标志
    }

    //标准优先级类
    int nNormal= n = ComboBox_AddString(hWndCtrl, TEXT("标准"));
    ComboBox_SetItemData(hWndCtrl, n, NORMAL_PRIORITY_CLASS);

    //“低于标准”优先级类
    if (dwpc == 0){
        n = ComboBox_AddString(hWndCtrl, TEXT("低于标准"));
        ComboBox_SetItemData(hWndCtrl, n, BELOW_NORMAL_PRIORITY_CLASS);
    }
    n = ComboBox_AddString(hWndCtrl, TEXT("空闲"));
    ComboBox_SetItemData(hWndCtrl, n, IDLE_PRIORITY_CLASS);
    ComboBox_SetCurSel(hWndCtrl, nNormal); //选中“标准”优先级

    //线程相对优先级组合框
    hWndCtrl = GetDlgItem(hwnd, IDC_THREADRELATIVEPRIORITY);
    n = ComboBox_AddString(hWndCtrl, TEXT("最高"));
    ComboBox_SetItemData(hWndCtrl, n, THREAD_PRIORITY_TIME_CRITICAL);

    n = ComboBox_AddString(hWndCtrl, TEXT(""));
    ComboBox_SetItemData(hWndCtrl, n, THREAD_PRIORITY_HIGHEST);

    n = ComboBox_AddString(hWndCtrl, TEXT("高于标准"));
    ComboBox_SetItemData(hWndCtrl, n, THREAD_PRIORITY_ABOVE_NORMAL);

    nNormal= n = ComboBox_AddString(hWndCtrl, TEXT("标准"));
    ComboBox_SetItemData(hWndCtrl, n, THREAD_PRIORITY_NORMAL);

    n = ComboBox_AddString(hWndCtrl, TEXT("低于标准"));
    ComboBox_SetItemData(hWndCtrl, n, THREAD_PRIORITY_BELOW_NORMAL);

    n = ComboBox_AddString(hWndCtrl, TEXT("空闲"));
    ComboBox_SetItemData(hWndCtrl, n, THREAD_PRIORITY_IDLE);

    ComboBox_SetCurSel(hWndCtrl, nNormal);

    Edit_LimitText(GetDlgItem(hwnd, IDC_SLEEPTIME), 4); //休眼最大值为9999

    return TRUE;
}

//////////////////////////////////////////////////////////////////////////
void Dlg_OnCommand(HWND hwnd, int id, HWND hwndCtrl, UINT codeNotity)
{
    switch (id)
    {
    case IDCANCEL:
        PostQuitMessage(0);
        break;

    case IDC_PROCESSPRIORITYCLASS:
        if (codeNotity == CBN_SELCHANGE){
            int ntp = (int)ComboBox_GetItemData(hwndCtrl, ComboBox_GetCurSel(hwnd));
            SetThreadPriority(GetCurrentThread(), ntp);
        }
        break;

    case IDC_THREADRELATIVEPRIORITY:
        if (codeNotity == CBN_SELCHANGE){
            int ntp =(int)ComboBox_GetItemData(hwndCtrl, ComboBox_GetCurSel(hwnd));
            SetThreadPriority(GetCurrentThread(),ntp);
        }
        break;

    case IDC_SUSPEND:
        //为了避免死锁,将“挂起”按钮禁用
        EnableWindow(hwndCtrl, FALSE);

        HANDLE hThreadPrimary; //主线程
        DuplicateHandle(GetCurrentProcess(), GetCurrentThread(), 
                        GetCurrentProcess(),&hThreadPrimary,
                        THREAD_SUSPEND_RESUME,FALSE,DUPLICATE_SAME_ACCESS);

        DWORD dwThreadID;
        CloseHandle(chBEGINTHREADEX(NULL, 0, ThreadFunc,
                        hThreadPrimary, 0, &dwThreadID));
        break;

    }
}

//////////////////////////////////////////////////////////////////////////
INT_PTR WINAPI Dlg_Proc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    switch (uMsg)
    {
        chHANDLE_DLGMSG(hwnd, WM_INITDIALOG, Dlg_OnInitDialog);
        chHANDLE_DLGMSG(hwnd, WM_COMMAND, Dlg_OnCommand);
    }
    return FALSE;
}

//////////////////////////////////////////////////////////////////////////
int WINAPI _tWinMain(HINSTANCE hInstExe, HINSTANCE, PTSTR szCmdLine, int)
{
    HWND hWnd = CreateDialog(hInstExe, MAKEINTRESOURCE(IDD_SCHEDLAB), NULL, Dlg_Proc); //非模态对话框
    BOOL fQuit = FALSE;

    while (!fQuit){
        MSG msg;

        if (PeekMessage(&msg,NULL,0,0,PM_REMOVE)){
            //用来IsDialogMessage判断是否是键盘导航(如Tab键)
            if (!IsDialogMessage(hWnd,&msg)){
                if (msg.message == WM_QUIT){
                    fQuit = TRUE;
                } else{
                    TranslateMessage(&msg);
                    DispatchMessage(&msg);
                }        
            } //End if(!IsDialogMessage())
        
        } else{

            //将数字加入列表框中
            static int s_n = -1;
            TCHAR sz[20];
            StringCchPrintf(sz, _countof(sz), TEXT("%u"), ++s_n);
            HWND hWndWork = GetDlgItem(hWnd, IDC_WORK);
            ListBox_SetCurSel(hWndWork, ListBox_AddString(hWndWork,sz));

            //如果列表框中的项目太多时,则删除前100项
            while (ListBox_GetCount(hWndWork) > 100)
                ListBox_DeleteString(hWndWork, 0);

            //获取线程休眠的时间
            int nSleep = GetDlgItemInt(hWnd, IDC_SLEEPTIME, NULL, FALSE);
            if (chINRANGE(1,nSleep,9999)){
                Sleep(nSleep);
            }
        }
    }

    DestroyWindow(hWnd);
    return 0;
}

//resource.h

//{{NO_DEPENDENCIES}}
// Microsoft Visual C++ 生成的包含文件。
// 供 7_SchedLab.rc 使用
//
#define IDD_SCHEDLAB                    101
#define IDI_SCHEDLAB                    102
#define IDC_PROCESSPRIORITYCLASS        1001
#define IDC_THREADRELATIVEPRIORITY      1002
#define IDC_SLEEPTIME                   1003
#define IDC_SUSPEND                     1004
#define IDC_WORK                        1005

// Next default values for new objects
// 
#ifdef APSTUDIO_INVOKED
#ifndef APSTUDIO_READONLY_SYMBOLS
#define _APS_NEXT_RESOURCE_VALUE        103
#define _APS_NEXT_COMMAND_VALUE         40001
#define _APS_NEXT_CONTROL_VALUE         1006
#define _APS_NEXT_SYMED_VALUE           101
#endif
#endif

//SchedLab.rc

// Microsoft Visual C++ generated resource script.
//
#include "resource.h"

#define APSTUDIO_READONLY_SYMBOLS
/////////////////////////////////////////////////////////////////////////////
//
// Generated from the TEXTINCLUDE 2 resource.
//
#include "winres.h"

/////////////////////////////////////////////////////////////////////////////
#undef APSTUDIO_READONLY_SYMBOLS

/////////////////////////////////////////////////////////////////////////////
// 中文(简体,中国) resources

#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_CHS)
LANGUAGE LANG_CHINESE, SUBLANG_CHINESE_SIMPLIFIED

#ifdef APSTUDIO_INVOKED
/////////////////////////////////////////////////////////////////////////////
//
// TEXTINCLUDE
//

1 TEXTINCLUDE 
BEGIN
    "resource.h"
END

2 TEXTINCLUDE 
BEGIN
    "#include ""winres.h""
"
    ""
END

3 TEXTINCLUDE 
BEGIN
    "
"
    ""
END

#endif    // APSTUDIO_INVOKED


/////////////////////////////////////////////////////////////////////////////
//
// Dialog
//

IDD_SCHEDLAB DIALOGEX 0, 0, 250, 98
STYLE DS_SETFONT | DS_MODALFRAME | DS_3DLOOK | DS_FIXEDSYS | DS_CENTER | WS_MAXIMIZEBOX | WS_VISIBLE | WS_CAPTION | WS_SYSMENU
EXSTYLE WS_EX_NOPARENTNOTIFY | WS_EX_CLIENTEDGE
CAPTION "Scheduling Lab"
FONT 8, "MS Shell Dlg", 400, 0, 0x1
BEGIN
    LTEXT           "线程相对优先级:",IDC_STATIC,12,34,65,8
    COMBOBOX        IDC_PROCESSPRIORITYCLASS,75,11,72,80,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP
    LTEXT           "进程优先级类:",IDC_STATIC,12,13,57,8
    COMBOBOX        IDC_THREADRELATIVEPRIORITY,75,32,72,80,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP
    LTEXT           "休眠(0至9999ms):",IDC_STATIC,12,56,62,8
    EDITTEXT        IDC_SLEEPTIME,75,54,71,14,ES_AUTOHSCROLL | ES_NUMBER
    PUSHBUTTON      "挂起",IDC_SUSPEND,47,75,50,14
    LISTBOX         IDC_WORK,161,8,79,82,LBS_NOINTEGRALHEIGHT | LBS_NOSEL | WS_TABSTOP
END


/////////////////////////////////////////////////////////////////////////////
//
// DESIGNINFO
//

#ifdef APSTUDIO_INVOKED
GUIDELINES DESIGNINFO
BEGIN
    IDD_SCHEDLAB, DIALOG
    BEGIN
        RIGHTMARGIN, 249
        TOPMARGIN, 2
        BOTTOMMARGIN, 97
    END
END
#endif    // APSTUDIO_INVOKED


/////////////////////////////////////////////////////////////////////////////
//
// Icon
//

// Icon with lowest ID value placed first to ensure application icon
// remains consistent on all systems.
IDI_SCHEDLAB            ICON                    "SchedLab.ico"
#endif    // 中文(简体,中国) resources
/////////////////////////////////////////////////////////////////////////////



#ifndef APSTUDIO_INVOKED
/////////////////////////////////////////////////////////////////////////////
//
// Generated from the TEXTINCLUDE 3 resource.
//


/////////////////////////////////////////////////////////////////////////////
#endif    // not APSTUDIO_INVOKED

7.11 亲缘性(Affinity)(也叫关联性)

(1)软关联:默认下,Vista给线程分配CPU时,在其他因素都一样的前提下,系统将使线程在上一次运行的处理上运行,这样有利于重用仍在处理器高速缓存中的数据。

(2)硬关联:通过SetProcessAffinityMaskSetThreadAffinityMask来指定进(线)程在哪些CPU上运行,获取进(线)程关联性掩码,可通过GetProcessAffinityMask或GetThreadAffinityMask函数。

(3)子进程继承进程的关联性。即如果一个进程关联性掩码为0x0000005,它的其所有子进程中的任何线程也将有相同的掩码,并与它共用同一组CPU。

(4)关联性掩码共MAXIMUNM_PROCESSORS位,从0-31/63分别对应CPU 0至CPU 31(63)

(5)硬关联性有时会影响到CPU的调度程序的调度方案(见课本194)。如高优先级的A线程被限制在一个CPU 0上,但如果CPU0还有更高优先级线程B,而另一个CPU1上运行的是低优先级线程C。当A被限制在这个CPU0时,就无法抢占另一CPU1上的C线程来执行。为了允许系统将线程移到另一个较空闲的CPU上,可以给线程设置一个理想的CPU。SetThreadIdealProcessor(hThread,dwIdealProcessor),其中dwIdealProcessor为希望将线程设置到的CPU。

(6)Windows任务管理器允许更改进程的CPU关联性

【ThreadAffinity程序】测试线程的亲缘性及优先级

#include <windows.h>
#include <tchar.h>
#include <locale.h>
#include <time.h>
#include <malloc.h>

#define CPUINDEXTOMASK(dwCPUIndex)  (1<<(dwCPUIndex))

//////////////////////////////////////////////////////////////////////////
//线程函数
DWORD WINAPI ThreadFunc(LPVOID lpParam)
{
    HANDLE hEvent = (HANDLE)lpParam;
    srand((unsigned int)time(NULL));
    float fRandAvg = 0.0f;
    float fCnt = 0.0f;

    while (TRUE)
    {
        fCnt += 1.0f;
        fRandAvg += (float)rand();
        fRandAvg /= fCnt;
        Sleep(1); //当注释该行时,CPU占用率将几乎达100%,这里可休眠一下

        if (WAIT_OBJECT_0 == WaitForSingleObject(hEvent, 0))
            break;
    }
    _tprintf(_T("%d个随机数的平均值为%f
"), (DWORD)fCnt, fRandAvg);

    return (DWORD)fCnt;
}

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

    //创建停止事件
    HANDLE hStopEvent = CreateEvent(NULL, TRUE, FALSE, NULL);

    //获取CPU个数
    SYSTEM_INFO si = {0};
    GetSystemInfo(&si);
    const DWORD dwCPUCnt = si.dwNumberOfProcessors;

    //创建线程句柄数组
    HANDLE *hThread =(HANDLE*)malloc(dwCPUCnt*sizeof(HANDLE));
    DWORD dwThreadID = 0;
    DWORD dwCPUIndex = 0;

    //循环创建线程
    for (DWORD i = 0; i < dwCPUCnt;i++){
        hThread[i] = CreateThread(NULL, 0, ThreadFunc,
                                  hStopEvent, CREATE_SUSPENDED, &dwThreadID);
        //设置子线程的亲缘性(将子线程分配在不同的CPU上)
        SetThreadAffinityMask(hThread[i], CPUINDEXTOMASK(i));

        _tprintf(_T("线程[ID:%d]运行在CPU(%d)上
"), dwThreadID, i);
        ResumeThread(hThread[i]);

        _tsystem(_T("PAUSE")); //暂停
    }

    //将第2个子线程的设为“高优先级”
    if (dwCPUCnt>1) SetThreadPriority(hThread[1], THREAD_PRIORITY_HIGHEST);

    //如果每个CPU都安排了一个线程的话,此时可应看到所有CPU占用率几乎都是100%
    _tprintf(_T("线程全部创建完毕,请查看任务管理器中CPU使用率!
"));
    _tsystem(_T("PAUSE")); //暂停

    //通知所有线程停止
    SetEvent(hStopEvent);

    //等待所有线程退出
    WaitForMultipleObjects(dwCPUCnt, hThread,TRUE, INFINITE);

    DWORD dwExitCode = 0;
    //取得线程的退出代码,此例中是循环次数,并关闭所有线程句柄
    for (DWORD i = 0; i < dwCPUCnt;i++){
        GetExitCodeThread(hThread[i], &dwExitCode);
        _tprintf(_T("线程[ID:%d]退出,退出码为:%u!
"),
                   GetThreadId(hThread[i]),dwExitCode);
        CloseHandle(hThread[i]);
    }

    //释放线程句柄数组
    free(hThread);

    //关闭停止事件句柄
    CloseHandle(hStopEvent);
    _tsystem(_T("PAUSE")); //暂停
    return 0;
}
原文地址:https://www.cnblogs.com/5iedu/p/4712638.html