7.1 线程的挂起和恢复
(1)线程挂起
①创建时(如CreateProcess、CreateThread),传入CREATE_SUSPENDED标志
②用SuspendThread挂起线程。这个函数可以挂起自己,也可以挂起其它线程(只要有线程句柄)
③调用SuspendThread时,如果这时线程执行在用户态,线程会马上被挂起。如果调用SuspendThread时线程己经执行在内核态时,SuspendThread会异步返回,而线程并不会马上暂停。但当该线程从内核态又转为用户态时,则会立即被暂停。
④当调用SuspendThread挂起线程时,我们并不知道线程在做什么?如果此时A线程正在分配堆中的内存,则它将会锁定堆,这会导致此时也要访问堆的B线程被中止,直到A恢复,而这可能引起其他问题或者死锁。所以只有在确切目标线程在哪里(或在做什么时)调用SuspendThread才是安全的。
⑤线程在挂机计数不为0或没有消息队列没有消息时,是不可调度的(没时间片)
(2)线程恢复:ResumeThread,返回前一个挂起计数,否则返回0xFFFFFFFF。
①一个线程可以被多次挂起,最多可以挂起MAXIMUNM_SUSPEND_COUNT(127)次
②线程被挂起多次时,只有恢复到挂起计数为0时,才可以重新被调度。
(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),正在输出... 时间%d "), 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("正在唤醒线程... ")); SuspendProcess(GetCurrentProcessId(), FALSE); _tsystem(_T("PAUSE")); //在线程运行过程, 按任意键可以挂起进程 _tprintf(_T("进程己被挂起! ")); SuspendProcess(GetCurrentProcessId(), TRUE); //因所有线程都被挂起,进程中无活动线程,故进程(线)程无法被唤醒 //所以后面的代码无法执行! //再次唤醒所有线程 _tprintf(_T("正在唤醒线程... ")); SuspendProcess(GetCurrentProcessId(), FALSE); _tsystem(_T("PAUSE")); return 0; }
7.3 睡眠——void Sleep(DWORD dwMilliseconds)函数
(1)调用Sleep函数,将使线程自愿放弃属于它的时间片中剩下的部分
(2)dwMilliseconds只是近似的毫秒数。因为Windows不是实时操作系统,可以过了这个时间段,系统仍在调用其他线程。
(3)dwMilliseconds为INFINITE时,表示永远不要调用这个线程(但这不好,如果不调用线程,应该让其退出!)
(4)dwMilliseconds为0时,主动放弃剩余时间片。但如果没有更高优先级线程(优先级≥本线程)可调度时,这个线程可能被重新调度,即使低优先级线程仍处在饥饿状态。
7.4 切换到另一个线程——BOOL SwitchToThread();
(1)调用此函数时,系统会查看是否存在正饥饿线程,如果没有立即返回,调用线程继续执行。如果有,则会调用其他线程(但与Sleep(0)不同,对另一个线程优先级没有要求,其优先级可以比调用函数的线程低)
(2)该函数允许一个需要资源的线程强制另一个优先级较低、而目前却拥有该资源的线程放弃该资源。
(3)返回值:如果没有其他线程可以调度,返回FALSE。否则返回非零
7.5 在超线程CPU上切换到另一个线程
(1)超线程处理器芯片上有多个“逻辑CPU”,每个都可以运行一个线程(注意,与多核CPU不同!)。这样的处理器一般需要多加入一个Logical CPU Pointer(逻辑处理单元),让每个线程都有自己的状态寄存器。而其余部分如ALU(整数运算单元)、FPU(浮点运算单元)、L2 Cache(二级缓存)则保持不变,这些部分是被共享的。
(2)虽然采用超线程技术能同时执行两个线程,但它并不象两个真正的CPU那样,每个CPU都具有独立的资源。当两个线程都同时需要某一个资源时,其中一个要暂时停止,并让出资源,直到这些资源闲置后才能继续。因此超线程的性能并不等于两颗CPU的性能。
(3)当一个线程中止时,CPU自动执行另一个线程,无需操作系统干预。
(4)在超线程CPU上执行一个旋转循环(spin loop)代码时,我们需要强制当前线程暂停,从而另一个线程可以使用芯片资源。在Win32API中,可以调用YieldProcessor宏来强制让出CPU。
7.6 线程的执行时间(3种方法:)
(1)求时间的3种方法
方法 |
函数举例 |
精度 |
基于一般计数器 |
GetTickCount GetProcessTimes GetThredTimes |
依赖系统内部10~15ms的计时器,可达毫秒级,精度低,也无法精确到1ms |
基于高精度晶振频率的时间戳计数器 (推存使用) |
QuerPerformanceCounter |
可达微秒级。精度较高 |
基于CPU频率的时间戳计数器 |
ReadTimeStampCounter |
精度可高达1/CPU主频(Hz) 秒,即纳秒级别 |
(2)与GetTickCount功能类似的函数(计时的时候会包含线程被中断的时间!)
函数 |
描述 |
单位 |
GetTickCount |
返回自计算机启动以来的毫秒数 |
单位为毫秒,精度低 |
QueryPerformanceCounter |
返回自计算机启动以来的高精度晶振的计数器 |
单位为计数值,须换算成时间(转换方法见后面) |
ReadTimeStampCounter |
返回自计算机启动以来的CPU时钟周期数 |
单位为计数值,须换算成时间(转换方法见后面) |
(3)占用CPU时间的函数比较
函数 |
描述 |
单位 |
GetProcessTimes GetThreadTimes |
返回进(线)程占用CPU的时间 (这个时间是基于一般计数器的) |
100ns |
QueryThreadCircleTime QueryProcessCircleTime |
返回进(线)程占用CPU的时钟周期数 (这个时钟周期计数是基于CPU频率) |
计数值,须转换为时间(转换方法见后面)。精度更高 |
【ThreadTimes程序】用来演示求线程执行时间的方法
#include <windows.h> #include <tchar.h> #include <malloc.h> #include <locale.h> #include <time.h> #define CPUIndexToMask(dwCPUIndex) (1<<(dwCPUIndex)) DWORD WINAPI ThreadProc(PVOID pParam) { DWORD dwStart = GetTickCount(); HANDLE hEvent = (HANDLE)pParam; srand((unsigned int)time(NULL)); float fRangeAvg = 0.0f; float fCnt = 0.0f; while (TRUE) { //将每次产生的随机数求平均值 fCnt += 1.0f; fRangeAvg += (float)rand(); fRangeAvg /= fCnt; // dwMilliseconds=0,线程会测试事件对象的状态,并立即返回。 if (WAIT_OBJECT_0 == WaitForSingleObject(hEvent, 0)) break; } //模拟一个释放CPU的操作,这个时间会被GetTickCount计算错误统计在内 Sleep(1000); DWORD dwEnd = GetTickCount(); _tprintf(_T("线程[ID:0x%X]运行时间(GetTickCount计算):%ums "), GetCurrentThreadId(),dwEnd-dwStart); 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; //创建线程 for (DWORD i = 0; i < dwCPUCnt;i++){ hThread[i] = CreateThread(NULL, 0, ThreadProc, hStopEvent, CREATE_SUSPENDED, &dwThreadID); //将线程分配在不同的CPU上 //SetThreadAffinityMask(hThread[i], CPUIndexToMask(i)); _tprintf(_T("线程[ID:0x%X]运行在CPU(%d)上 "), dwThreadID, i); ResumeThread(hThread[i]); } //如果每个CPU都安排了一个线程的话,此时应看到所有CPU占用率几乎都是100% _tprintf(_T("共%d个线程,全部创建完毕,请查看任务管理器中CPU使用率 "), dwCPUCnt); _tsystem(_T("PAUSE")); //通知所有的线程停止 SetEvent(hStopEvent); //等待所有线程退出 WaitForMultipleObjects(dwCPUCnt, hThread, TRUE, INFINITE); DWORD dwExitCode; FILETIME tmCreation = {0}; FILETIME tmExit = { 0 }; FILETIME tmKernel = { 0 }; FILETIME tmUser = { 0 }; SYSTEMTIME tmSys = { 0 }; ULARGE_INTEGER bigTmp1 = { 0 };//2个DWORD,即64位 ULARGE_INTEGER bigTmp2 = { 0 }; //取出线程退出代码,此例中就是每个线程内循环的次数。 //统计线程运行时间,并关闭所有线程内核对象 for (DWORD i = 0; i < dwCPUCnt;i++){ GetExitCodeThread(hThread[i], &dwExitCode); _tprintf(_T("线程[H:0x%08X]退出,退出码(%u),以下为时间统计; "), hThread[i],dwExitCode); GetThreadTimes(hThread[i], &tmCreation, &tmExit, &tmKernel, &tmUser); //创建时间 FileTimeToLocalFileTime(&tmCreation, &tmCreation); FileTimeToSystemTime(&tmCreation, &tmSys); _tprintf(_T(" 创建时间:%02u:%02u:%02u.%04u "), tmSys.wHour,tmSys.wMinute,tmSys.wSecond,tmSys.wMilliseconds); //退出时间 FileTimeToLocalFileTime(&tmExit, &tmExit); FileTimeToSystemTime(&tmExit, &tmSys); _tprintf(_T(" 退出时间:%02u:%02u:%02u.%04u "), tmSys.wHour, tmSys.wMinute, tmSys.wSecond, tmSys.wMilliseconds); //线程存活时间(=线程退出时间-创建时间) //FILETIME中的时间单位为100ns,除以10000才是毫秒 bigTmp1.HighPart = tmCreation.dwHighDateTime; bigTmp1.LowPart = tmCreation.dwLowDateTime; bigTmp2.HighPart = tmExit.dwHighDateTime; bigTmp2.LowPart = tmExit.dwLowDateTime; _tprintf(_T(" 线程存活时间:%I64dms "), (bigTmp2.QuadPart-bigTmp1.QuadPart)/10000); //内核时间 bigTmp1.HighPart = tmKernel.dwHighDateTime; bigTmp1.LowPart = tmKernel.dwLowDateTime; _tprintf(_T(" 内核模式(RING0)耗时:%I64dms "),bigTmp1.QuadPart /10000); //用户时间 bigTmp2.HighPart = tmUser.dwHighDateTime; bigTmp2.LowPart = tmUser.dwLowDateTime; _tprintf(_T(" 用户模式(RING3)耗时:%I64dms "), bigTmp2.QuadPart / 10000); //实际占用CPU时间(=内核耗时+用户耗时) bigTmp2.HighPart = tmUser.dwHighDateTime; bigTmp2.LowPart = tmUser.dwLowDateTime; _tprintf(_T(" 实际占用CPU时间:%I64dms "), (bigTmp1.QuadPart+bigTmp2.QuadPart) / 10000); //关闭线程句柄 CloseHandle(hThread[i]); } free(hThread); CloseHandle(hStopEvent); _tsystem(_T("PAUSE")); return 0; }
(3)基于晶振频率计数器转换为时间的方法(方法见TimeStampCounter程序的TStopWatch类)
//CStopWatch晶振计时类,基于主板晶振频率的时间戳计数器的时间类 class CStopWatch{ private: LARGE_INTEGER m_liPerfFreq; //频率,表示每秒的计数值 LARGE_INTEGER m_liPerfStart; //调用Start函数时的计数值 public: CStopWatch(); //构造函数 void Start(); __int64 Now() const; //计算自调用Start()函数以来的毫秒数 __int64 NowInMicro() const; //计数自调用Start()函数以来的微秒数 }; CStopWatch::CStopWatch(){ QueryPerformanceFrequency(&m_liPerfFreq);//求晶振的频率(次数/秒) Start(); } void CStopWatch::Start(){ QueryPerformanceCounter(&m_liPerfStart); //开始时的计数值 } __int64 CStopWatch::Now() const{ //调用Start函数以来的毫秒数 LARGE_INTEGER liPerfNow; // QueryPerformanceCounter(&liPerfNow); //将次数转为时间(毫秒),注意频率的单位为秒 return ((liPerfNow.QuadPart - m_liPerfStart.QuadPart)*1000) / m_liPerfFreq.QuadPart; } __int64 CStopWatch::NowInMicro() const{ LARGE_INTEGER liPerfNow; // QueryPerformanceCounter(&liPerfNow); //将次数转为时间(微秒),注意频率的单位为秒 return ((liPerfNow.QuadPart - m_liPerfStart.QuadPart) * 1000000) / m_liPerfFreq.QuadPart; }
(4)基于CPU频率的时间戳计数器转换为时间的方法
①时间戳计数器简介(Time Stamp Counter,TSC)
A、该计时器为自计算机启动以来的时钟周期数(计数值)
B、从Pentium开始,所有的Intel 80x86 CPU就都又包含一个64位的时间戳计数器(TSC)的寄存器。该寄存器实际上是一个不断增加的计数器,它在CPU的每个时钟信号到来时加1(也即每一个 clock-cycle输入CPU时,该计数器的值就加1)。
C、利用CPU的TSC,操作系统通常可以得到更为精准的时间度量。假如clock-cycle的频率是400MHZ,那么TSC就将每2.5纳秒增加一次。
D、可用宏ReadTimeStampCounter或内部函数__rdtsc来获取当前TSC值。
②TSC计数值转化为时间的方法:ReadTimeStampCounter/CPU主频
③求CPU主频的两种方法:
A、查注册表法:HKLM→HARDAWARE→DESCRIPTION→System→CentralProcessor→0→~MHz
B、通过TStopWatch类求CPU主频的近似值:(方法见TimeStampCounter程序的GetCPUFrequencyInMHz函数)
//求CPU主频,注意这是基于CPU的主频,与主板晶振的频率QueryPerformanceFrequency属不同概念! DWORD GetCPUFrequencyInMHz(){ //改变线程优先级以确保线程当Sleep()后有更多的调度时间 int currentPriority = GetThreadPriority(GetCurrentThread()); SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_HIGHEST); __int64 elapsedTime = 0; //己耗时 CStopWatch stopWatch; //利用晶振计时类来求CPU主频 //默认为当前时间 __int64 perfStartTime = stopWatch.NowInMicro(); //获取当前时CPU时间戳的计数值 unsigned __int64 cyclesOnStart = ReadTimeStampCounter(); //等待1秒,以便求1秒左右的时间间隔内CPU主频的平均值 Sleep(1000); //获取1秒后的己耗的CPU时间戳周期数 unsigned __int64 numberOfCycles = ReadTimeStampCounter() - cyclesOnStart; //利用晶振计时类求时间间隔 elapsedTime = stopWatch.NowInMicro() - perfStartTime; //恢复线程的优先级 SetThreadPriority(GetCurrentThread(), currentPriority); //计数CPU主频 = 己耗的CPU周期数/时间 (单位:次/μs 或 MHz) //注意:1MHz=1000000Hz,1s=1000000μs DWORD dwCPUFrequency = (DWORD)(numberOfCycles / elapsedTime); return dwCPUFrequency; }
④该种方法的缺点:精度虽高,但数据抖动比较厉害,在节能模式的时候结果偏慢,超频模式的时候又偏快,而且用电池和接电源的时候效果也不一样。此外用这种方法求时间还得考虑用SetThreadPriority提高优先级尽量独占时间片。并使用SetThreadAffinityMask以确保每次调用QueryPerformanceCounter的时候在同一CPU核心上。
【TimeStampCounter程序】利用TSC来计算线程时间
#include <windows.h> #include <tchar.h> #include <locale.h> #include <time.h> #include "StopWatch.h" ////////////////////////////////////////////////////////////////////////// //线程函数 DWORD WINAPI ThreadProc(PVOID pvParam) { CStopWatch sw; HANDLE hEvent = (HANDLE)pvParam; srand((unsigned int)time(NULL)); float fRangeAvg = 0.0f; float fCnt = 0.0f; while (TRUE){ //将每次产生的随机数求平均值 fCnt += 1.0f; fRangeAvg += (float)rand(); fRangeAvg /= fCnt; if (WAIT_OBJECT_0 ==WaitForSingleObject(hEvent, 0)) break; } //模拟一个释放CPU的操作,这个时间会被GetTickCount计算错误统计在内 Sleep(1000); __int64 elapseTime = sw.NowInMicro(); return (DWORD)elapseTime; } int _tmain() { _tsetlocale(LC_ALL, _T("chs")); DWORD dwCPUFreq; dwCPUFreq = GetCPUFrequencyInMHz(); //创建事件对象,用于等待线程退出 HANDLE hEvent = CreateEvent(NULL, FALSE, FALSE, NULL); HANDLE hThread = CreateThread(NULL, 0, ThreadProc, hEvent, 0, NULL); _tprintf(_T("线程正在运行中,请按任意键结束! ")); _gettchar(); //通知线程结束 SetEvent(hEvent); WaitForSingleObject(hThread, INFINITE); ULONG64 threadCircleTime; QueryThreadCycleTime(hThread, &threadCircleTime); DWORD dwElapseTime; GetExitCodeThread(hThread, &dwElapseTime); LARGE_INTEGER liFreq; QueryPerformanceFrequency(&liFreq); _tprintf(_T(" 运行信息(TSC法): ")); _tprintf(_T(" CPU主频 :%dMHz "), dwCPUFreq); _tprintf(_T(" 晶振频率:%dMHz "), liFreq.QuadPart / 1000); _tprintf(_T(" 线程存活时间为:%dms(%dμs) "), dwElapseTime / 1000, dwElapseTime); _tprintf(_T(" 线程占用的CPU计数:%I64d(次) "), threadCircleTime); _tprintf(_T(" 线程占用的CPU时间:%I64d(μs) "), threadCircleTime / dwCPUFreq); FILETIME tmCreation = { 0 }; FILETIME tmExit = { 0 }; FILETIME tmKernel = { 0 }; FILETIME tmUser = { 0 }; SYSTEMTIME tmSys = { 0 }; ULARGE_INTEGER bigTmp1 = { 0 };//2个DWORD,即64位 ULARGE_INTEGER bigTmp2 = { 0 }; GetThreadTimes(hThread, &tmCreation, &tmExit, &tmKernel, &tmUser); _tprintf(_T(" 运行信息(GetThreadTimes法): ")); bigTmp1.HighPart = tmCreation.dwHighDateTime; bigTmp1.LowPart = tmCreation.dwLowDateTime; bigTmp2.HighPart = tmExit.dwHighDateTime; bigTmp2.LowPart = tmExit.dwLowDateTime; _tprintf(_T(" 线程存活时间:%I64dms "), (bigTmp2.QuadPart - bigTmp1.QuadPart) / 10000); bigTmp1.HighPart = tmKernel.dwHighDateTime; bigTmp1.LowPart = tmKernel.dwLowDateTime; bigTmp2.HighPart = tmUser.dwHighDateTime; bigTmp2.LowPart = tmUser.dwLowDateTime; _tprintf(_T(" 线程占用的CPU时间:%I64d(μs) "), (bigTmp1.QuadPart + bigTmp2.QuadPart)/10); CloseHandle(hEvent); CloseHandle(hThread); _tsystem(_T("PAUSE")); return 0; }
//StopWatch.h
#pragma once #include <windows.h> ////////////////////////////////////////////////////////////////////////// //CStopWatch晶振计时类,基于主板晶振频率的时间戳计数器的时间类 class CStopWatch{ private: LARGE_INTEGER m_liPerfFreq; //频率,表示每秒的计数值 LARGE_INTEGER m_liPerfStart; //调用Start函数时的计数值 public: CStopWatch(); //构造函数 void Start(); __int64 Now() const; //计算自调用Start()函数以来的毫秒数 __int64 NowInMicro() const; //计数自调用Start()函数以来的微秒数 }; ////////////////////////////////////////////////////////////////////////// DWORD GetCPUFrequencyInMHz();
//StopWatch.c
#include "StopWatch.h" ////////////////////////////////////////////////////////////////////////// CStopWatch::CStopWatch(){ QueryPerformanceFrequency(&m_liPerfFreq);//求晶振的频率(次数/秒) Start(); } void CStopWatch::Start(){ QueryPerformanceCounter(&m_liPerfStart); //开始时的计数值 } __int64 CStopWatch::Now() const{ //调用Start函数以来的毫秒数 LARGE_INTEGER liPerfNow; // QueryPerformanceCounter(&liPerfNow); //将次数转为时间(毫秒),注意频率的单位为秒 return ((liPerfNow.QuadPart - m_liPerfStart.QuadPart) * 1000) / m_liPerfFreq.QuadPart; } __int64 CStopWatch::NowInMicro() const{ LARGE_INTEGER liPerfNow; // QueryPerformanceCounter(&liPerfNow); //将次数转为时间(微秒),注意频率的单位为秒 return ((liPerfNow.QuadPart - m_liPerfStart.QuadPart) * 1000000) / m_liPerfFreq.QuadPart; } ////////////////////////////////////////////////////////////////////////// //求CPU主频,注意这是基于CPU的主频,与主板晶振的频率QueryPerformanceFrequency属不同概念! DWORD GetCPUFrequencyInMHz(){ //改变线程优先级以确保线程当Sleep()后有更多的调度时间 int currentPriority = GetThreadPriority(GetCurrentThread()); SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_HIGHEST); __int64 elapsedTime = 0; //己耗时 CStopWatch stopWatch; //利用晶振计时类来求CPU主频 //默认为当前时间 //获取当前时CPU时间戳的计数值 unsigned __int64 cyclesOnStart = ReadTimeStampCounter(); //等待1000ms,以便求1秒左右的时间间隔内CPU主频的平均值 Sleep(1000); //获取1秒后的己耗的CPU时间戳周期数 unsigned __int64 numberOfCycles = ReadTimeStampCounter() - cyclesOnStart; //利用晶振计时类求时间间隔 elapsedTime = stopWatch.NowInMicro();//单位是μs //恢复线程的优先级 SetThreadPriority(GetCurrentThread(), currentPriority); //计数CPU主频 = 己耗的CPU周期数/时间 (单位:次/μs 或 MHz) //注意:1MHz=1000000Hz,1s=1000000μs DWORD dwCPUFrequency = (DWORD)(numberOfCycles / elapsedTime); return dwCPUFrequency; }