• 第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;
    }
  • 相关阅读:
    windows下使用vscode编写运行以及调试C/C++
    nginx基础模块
    Windows下配置nginx+php(wnmp)
    快速创建 Vue 项目
    你真的会玩SQL吗?冷落的Top和Apply
    你真的会玩SQL吗?透视转换的艺术
    你真的会玩SQL吗?你所不知道的 数据聚合
    你真的会玩SQL吗?简单的数据修改
    你真的会玩SQL吗?表表达式,排名函数
    你真的会玩SQL吗?Case也疯狂
  • 原文地址:https://www.cnblogs.com/5iedu/p/4712638.html
Copyright © 2020-2023  润新知