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 #include#include#include#include
//挂起进程中所有的线程
voidSuspendProcess(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);
}elseResumeThread(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;
}
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 #include#include#include#include
#define CPUIndexToMask(dwCPUIndex) (1<
{
DWORD dwStart=GetTickCount();
HANDLE hEvent=(HANDLE)pParam;
srand((unsignedint)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\n"),
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)上\n"), dwThreadID, i);
ResumeThread(hThread[i]);
}//如果每个CPU都安排了一个线程的话,此时应看到所有CPU占用率几乎都是100%
_tprintf(_T("共%d个线程,全部创建完毕,请查看任务管理器中CPU使用率\n"), 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),以下为时间统计;\n"),
hThread[i],dwExitCode);
GetThreadTimes(hThread[i],&tmCreation, &tmExit, &tmKernel, &tmUser);//创建时间
FileTimeToLocalFileTime(&tmCreation, &tmCreation);
FileTimeToSystemTime(&tmCreation, &tmSys);
_tprintf(_T("\t创建时间:%02u:%02u:%02u.%04u\n"),
tmSys.wHour,tmSys.wMinute,tmSys.wSecond,tmSys.wMilliseconds);//退出时间
FileTimeToLocalFileTime(&tmExit, &tmExit);
FileTimeToSystemTime(&tmExit, &tmSys);
_tprintf(_T("\t退出时间:%02u:%02u:%02u.%04u\n"),
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("\t线程存活时间:%I64dms\n"),
(bigTmp2.QuadPart-bigTmp1.QuadPart)/10000);//内核时间
bigTmp1.HighPart =tmKernel.dwHighDateTime;
bigTmp1.LowPart=tmKernel.dwLowDateTime;
_tprintf(_T("\t内核模式(RING0)耗时:%I64dms\n"),bigTmp1.QuadPart /10000);//用户时间
bigTmp2.HighPart =tmUser.dwHighDateTime;
bigTmp2.LowPart=tmUser.dwLowDateTime;
_tprintf(_T("\t用户模式(RING3)耗时:%I64dms\n"), bigTmp2.QuadPart / 10000);//实际占用CPU时间(=内核耗时+用户耗时)
bigTmp2.HighPart =tmUser.dwHighDateTime;
bigTmp2.LowPart=tmUser.dwLowDateTime;
_tprintf(_T("\t实际占用CPU时间:%I64dms\n"),
(bigTmp1.QuadPart+bigTmp2.QuadPart) / 10000);//关闭线程句柄
CloseHandle(hThread[i]);
}free(hThread);
CloseHandle(hStopEvent);
_tsystem(_T("PAUSE"));return 0;
}
(3)基于晶振频率计数器转换为时间的方法(方法见TimeStampCounter程序的TStopWatch类)
//CStopWatch晶振计时类,基于主板晶振频率的时间戳计数器的时间类
classCStopWatch{private:
LARGE_INTEGER m_liPerfFreq;//频率,表示每秒的计数值
LARGE_INTEGER m_liPerfStart; //调用Start函数时的计数值
public:
CStopWatch();//构造函数
voidStart();
__int64 Now()const; //计算自调用Start()函数以来的毫秒数
__int64 NowInMicro() const; //计数自调用Start()函数以来的微秒数
};
CStopWatch::CStopWatch(){
QueryPerformanceFrequency(&m_liPerfFreq);//求晶振的频率(次数/秒)
Start();
}voidCStopWatch::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);returndwCPUFrequency;
}
④该种方法的缺点:精度虽高,但数据抖动比较厉害,在节能模式的时候结果偏慢,超频模式的时候又偏快,而且用电池和接电源的时候效果也不一样。此外用这种方法求时间还得考虑用SetThreadPriority提高优先级尽量独占时间片。并使用SetThreadAffinityMask以确保每次调用QueryPerformanceCounter的时候在同一CPU核心上。
【TimeStampCounter程序】利用TSC来计算线程时间
#include #include#include#include#include"StopWatch.h"
//
//线程函数
DWORD WINAPI ThreadProc(PVOID pvParam)
{
CStopWatch sw;
HANDLE hEvent=(HANDLE)pvParam;
srand((unsignedint)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("线程正在运行中,请按任意键结束!\n"));
_gettchar();//通知线程结束
SetEvent(hEvent);
WaitForSingleObject(hThread, INFINITE);
ULONG64 threadCircleTime;
QueryThreadCycleTime(hThread,&threadCircleTime);
DWORD dwElapseTime;
GetExitCodeThread(hThread,&dwElapseTime);
LARGE_INTEGER liFreq;
QueryPerformanceFrequency(&liFreq);
_tprintf(_T("\n运行信息(TSC法):\n"));
_tprintf(_T("\tCPU主频 :%dMHz\n"), dwCPUFreq);
_tprintf(_T("\t晶振频率:%dMHz\n"), liFreq.QuadPart / 1000);
_tprintf(_T("\t线程存活时间为:%dms(%dμs)\n"), dwElapseTime / 1000, dwElapseTime);
_tprintf(_T("\t线程占用的CPU计数:%I64d(次)\n"), threadCircleTime);
_tprintf(_T("\t线程占用的CPU时间:%I64d(μs)\n"), 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("\n运行信息(GetThreadTimes法):\n"));
bigTmp1.HighPart=tmCreation.dwHighDateTime;
bigTmp1.LowPart=tmCreation.dwLowDateTime;
bigTmp2.HighPart=tmExit.dwHighDateTime;
bigTmp2.LowPart=tmExit.dwLowDateTime;
_tprintf(_T("\t线程存活时间:%I64dms\n"),
(bigTmp2.QuadPart- bigTmp1.QuadPart) / 10000);
bigTmp1.HighPart=tmKernel.dwHighDateTime;
bigTmp1.LowPart=tmKernel.dwLowDateTime;
bigTmp2.HighPart=tmUser.dwHighDateTime;
bigTmp2.LowPart=tmUser.dwLowDateTime;
_tprintf(_T("\t线程占用的CPU时间:%I64d(μs)\n"),
(bigTmp1.QuadPart+ bigTmp2.QuadPart)/10);
CloseHandle(hEvent);
CloseHandle(hThread);
_tsystem(_T("PAUSE"));return 0;
}
//StopWatch.h
#pragma once#include
//
//CStopWatch晶振计时类,基于主板晶振频率的时间戳计数器的时间类
classCStopWatch{private:
LARGE_INTEGER m_liPerfFreq;//频率,表示每秒的计数值
LARGE_INTEGER m_liPerfStart; //调用Start函数时的计数值
public:
CStopWatch();//构造函数
voidStart();
__int64 Now()const; //计算自调用Start()函数以来的毫秒数
__int64 NowInMicro() const; //计数自调用Start()函数以来的微秒数
};//
DWORD GetCPUFrequencyInMHz();
//StopWatch.c
#include "StopWatch.h"
//
CStopWatch::CStopWatch(){
QueryPerformanceFrequency(&m_liPerfFreq);//求晶振的频率(次数/秒)
Start();
}voidCStopWatch::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);returndwCPUFrequency;
}