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结构体相应的标志
②Get\SetThreadContext;
【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寄存器状态:\n")); _tprintf(_T("\tEAX=0x%08X,EBX=0x%08X\n"),ct.Eax,ct.Ebx); _tprintf(_T("\tECX=0x%08X,EDX=0x%08X\n"), ct.Ecx, ct.Edx); _tprintf(_T("\tESI=0x%08X,EDI=0x%08X\n"), ct.Esi, ct.Edi); _tprintf(_T("\tEIP=0x%08X,ESP=0x%08X\n"), ct.Eip, ct.Esp); _tprintf(_T("\tEBP=0x%08X,EFL=0x%08X\n"), 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来允许或禁止系统动态提升优先级的做法。当然也可以用GetProcess\ThreadPriorityBoost来查看是否启动这种行为。
(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( "主线程被挂起,将不再响应输入和产生输出.\n" "按“确定”按钮恢复主线程,并退出子线程。\n" ); 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\0" END 2 TEXTINCLUDE BEGIN "#include ""winres.h""\r\n" "\0" END 3 TEXTINCLUDE BEGIN "\r\n" "\0" 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)硬关联:通过SetProcessAffinityMask\SetThreadAffinityMask来指定进(线)程在哪些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\n"), (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)上\n"), dwThreadID, i); ResumeThread(hThread[i]); _tsystem(_T("PAUSE")); //暂停 } //将第2个子线程的设为“高优先级” if (dwCPUCnt>1) SetThreadPriority(hThread[1], THREAD_PRIORITY_HIGHEST); //如果每个CPU都安排了一个线程的话,此时可应看到所有CPU占用率几乎都是100% _tprintf(_T("线程全部创建完毕,请查看任务管理器中CPU使用率!\n")); _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!\n"), GetThreadId(hThread[i]),dwExitCode); CloseHandle(hThread[i]); } //释放线程句柄数组 free(hThread); //关闭停止事件句柄 CloseHandle(hStopEvent); _tsystem(_T("PAUSE")); //暂停 return 0; }