如果你的win客户端让用户的cpu居高不下是很讨厌的...
鸟人最近遇到这个问题,发现如下两种简单的解决方案(项目中用的是第一种,第二种尚未实践,貌似IBM的World Community Grid就有用到后者),书下备忘...
1.用GetSystemInfo()获取系统信息,获取的结构体信息中有一字段是cpu个数(据我测试,是物理CPU个数,不是用超线程等手段呈现出来的核数),如果用户有多颗物理CPU可以让程序放肆一下(程序顶多把一颗物理CPU跑满),如果本来就只有一颗物理CPU可以在程序的线程循环、或者最密集的循环逻辑(for/while/...)内部调用SwitchToThread()方法出让CPU
2.降低程序的优先级到标准以下,这样可以让用户别的进程有机会运行
系统按照线程来调度,可以在进程内SetThreadPriority()调低线程相对优先级使过分的线程出让CPU,不过这样只能在进程的优先级类的范围里面进行波动
进程的优先级类可以通过SetPriorityClass()进行设置
具体的进程优先级类、线程相对优先级可以参考MSDN和下面这篇文章(转载来的)
每个线程都有一个“优先级”,范围是0~31,0为最低优先级,31为最高优先级。当系统决定哪个线程需要调度的时候,首先查看是否存在优先级为31的可调度线程,如果存在,就从中选择一个进行调度。当该线程的时间片到达之后,系统查看是否存在另一个优先级为31的可调度线程,如果存在,就调度它。
只要有一个可调度的优先级为31的线程存在,那么系统绝对不会调度优先级为0~30的线程,这样会导致其他线程“饥饿”。
高优先级线程往往“打断”低优先级线程的执行。比如,当一个优先级为15的线程正在运行,如果此时系统发现一个优先级比15高的线程可以调度,那么该高优先级线程会“打断”那个低优先级线程的执行,哪怕低优先级的时间片才过了一半。
另外,当系统引导的时候,系统创建一个特殊的线程,称为“zero page”(0页)线程,该线程是整个系统中唯一一个优先级为0(最低)的线程。当系统没有任何线程需要执行的时候,该线程负责将系统中所有RAM页面清零(也就是资源回收)。
线程优先级有一个抽象层的概念。
由于一些历史的原因,微软并没有将线程调度程序的行为完全固定下来。微软没有让应用程序充分利用调度程序的特性。微软宣称这个调度程序的行为是变化,在编程的时候需要注意。
由这几点可见,你的应用程序可以有自己的调度特性。
Windows API充分反映了系统调度的一个抽象层。如此,就不会直接与系统的调度程序通信,相反,可以调用API函数,根据系统的版本的不同转换这些参数。这一层,就是线程优先级的抽象层。
下面详细叙述这个抽象层究竟有哪些内容。
对于进程而言,Windows有一个“优先级类”的概念。这些优先级类作用与进程中的所有线程。Windows 2000/XP/2003/Vista支持6个“优先级类”:
1、Real-time:实时
2、High:高
3、Above normal:高于标准
4、Normal:标准
5、Below normal:低于标准
6、Idle:空闲。
一个进程应该避免使用“实时”优先级类,因为使用该优先级类会导致其他进程中的线程很难被调度,甚至会打断或者抢占系统线程的执行。“高”优先级类也应该尽量避免,除非有特殊的工作需要使用这个优先级。
当一个进程的“优先级类”被确定以后,就只需要考虑该进程内部各个线程之间的优先级关系。
对于进程中的线程而言,有一个“相对线程优先级”的概念,这可以决定一个进程中多个线程之间的优先级关系。
Windows支持7种“相对线程优先级”:
1、Time-critical:关键时间(最高的相对线程优先级)
2、Heightest:最高(翻译是这么翻译,但是并不是最高的相对线程优先级)
3、Above normal:高于标准
4、Normal:标准
5、Below normal:低于标准
6、Lowest:最低(翻译是这么翻译,但是并不是最低的相对线程优先级)
7、Idle:空闲
这里并没有提到有关0~31的优先级的任何内容。开发者从来不用具体设置一个线程的优先级,也就是不需要将一个线程优先级设置为0~31中的一个。操作系统负责将“优先级类”和“相对线程优先级”映射到一个具体的优先级上。这种映射方式,是随Windows版本的不同而不同的。
以下是Windows 2000/XP/2003/Vista的线程优先级映射方式(书上只说是Vista,但是对比本书第四版,可知2000和Vista是一样的):
线程相对 优先级 |
进程优先级类 |
|||||
---|---|---|---|---|---|---|
Idle |
Below Normal |
Normal |
Above Normal |
High |
Real-Time |
|
Time-critical |
15 |
15 |
15 |
15 |
15 |
31 |
Highest |
6 |
8 |
10 |
12 |
15 |
26 |
Above normal |
5 |
7 |
9 |
11 |
14 |
25 |
Normal |
4 |
6 |
8 |
10 |
13 |
24 |
Below normal |
3 |
5 |
7 |
9 |
12 |
23 |
Lowest |
2 |
4 |
6 |
8 |
11 |
22 |
Idle |
1 |
1 |
1 |
1 |
1 |
16 |
仔细查看该表,现在知道为什么最好不要将“进程优先级类”设置为“实时”了吧,因为一个进程如果具有“实时”的优先级类,那么该进程中的所有线程的优先级(最低也有16)比任何具有其他优先级类的进程中的线程的优先级(最高只有15)都要高。这样会导致其他优先级类低于“实时”的进程中的线程无法得到调度。
要注意的是,“优先级类”是一个抽象的概念,是对于一个进程而言的,但是不是说进程是可以调度的,只有线程是可以调度的。微软提出这个概念,仅仅只是为了帮助你将它与调度程序的内部运行的情况区分起来。“优先级类”应该是可以影响一个进程中所有线程的优先级的。
上面讲了有关线程优先级的内容,包括线程的“具体优先级”,“优先级抽象层”的内容(进程“优先级类”、“线程相对优先级”等)。
下面主要讲述如何在编程中设置线程的优先级。
一个进程,往往关联一个“优先级类”,你可以在CreateProcess函数的fdwCreate参数中设置这个优先级类的具体内容,可以有6种选择,对于6种优先级类:
1、REALTIME_PRIORITY_CLASS:Real-time,实时优先级类
2、HIGH_PRIORITY_CLASS:High,高优先级类
3、ABOVE_NORMAL_PRIORITY_CLASS:Above normal,高于标准
4、NORMAL_PRIORITY_CLASS:Normal,标准
5、BELOW_NORMAL_PRIORITY_CLASS:Below normal:低于标准
6、IDLE_PRIORITY_CLASS:Idle,空闲
你也可以更改一个特定的进程优先级类,通过呼叫SetPriorityClass函数可以到达这个目的。
BOOL SetPriorityClass(
HANDLE hProcess, //指定的进程句柄
DWORD fdwPriority); //优先级类(对应上面6种取值)
例如,下面的代码设置当前进程的优先级类为空闲:
SetPrioriytClass(GetCurrentProcess(), IDLE_PRIORITY_CLASS);
当然,可以获得某一个进程的优先级类,通过呼叫GetPriorityClass函数:
DWORD GetPriorityClass(HANDLE hProcess);
该函数返回的值就是对应6个优先级类的值中的一个。
当一个线程被创建,它的“线程相对优先级”默认为normal(标准)。CreateThread函数没有提供设置线程相对优先级的功能。可以调用SetThreadPriority函数设置一个线程的相对优先级。
BOOL SetThreadPriority(
HANDLE hThread, //指定的线程句柄
int nPriority); //线程相对优先级(取值对应如下)
该函数接受一个线程句柄和线程相对优先级取值,设置对应的线程的相对优先级。该线程相对优先级取值如下:
1、THREAD_PRIORITY_TIME_CRITICAL:Time-critical,关键时间(最高)
2、THREAD_PRIORITY_HIGHEST:Highest,最高(其实是“次高”)
3、THREAD_PRIORITY_ABOVE_NORMAL:Above normal,高于标准
4、THREAD_PRIORITY_NORMAL:Normal,标准
5、THREAD_PRIORITY_BELOW_NORMAL:Below normal,低于标准
6、THREAD_PRIORITY_LOWEST:Lowest,最低(其实是“次低”)
7、THREAD_PRIORITY_IDLE:Idle,空闲(最低)
你可以呼叫GetTreadPriotiry函数取得一个特定线程的相对优先级。
int GetThreadPriority(HANDLE hThread); //函数返回上述7个取值
为了创建一个线程相对优先级不是标准的线程,比如要创建一个高于标准的线程,你可以传递CREATE_SUSPENDED参数给CreateThread,从而创建一个起始状态为“挂起”的线程,然后调用SetThreadPriority函数设置该线程的相对优先级,然后调用ResumeThread函数恢复该线程的运行。代码如下:
DWORD dwThreadID;
HANDLE hThread = CreateThread(NULL, 0, ThreadFunc, NULL,
CREATE_SUSPENDED, &dwThreadID);
SetThreadPriority(hThread, THREAD_PRIORITY_ABOVE_NORMAL);
ResumeThread(hThread);
CloseHandle(hThread);
操作系统会动态地提高线程的基础优先级等级(0~31),一般是为了响应一些I/O事件。
有的时候,系统动态地提高线程优先级会带来不便。你可以呼叫SetProcessPriorityBoost和SetThreadPriorityBoost函数来通知系统是否需要动态提高线程优先级。
BOOL SetProcessPriorityBoost(
HANDLE hProcess,
BOOL bDisablePriorityBoost);
BOOL SetThreadPriorityBoost(
HANDLE hThread,
BOOL bDisablePriorityBoost);
这两个函数的第二个参数用于通知系统是否要动态提高指定进程的优先级类和指定线程的相对优先级。
也可以使用以下两个函数得到这些信息:
BOOL GetProcessPriorityBoost(
HANDLE hProcess,
PBOOL pbDisablePriorityBoost);
BOOL GetThreadPriorityBoost(
HANDLE hThread,
PBOOL pbDisablePriorityBoost);
下面是鸟人封装的获取系统资源使用情况的函数
//SysMonitor.h #ifndef _SYS_MONITOR_H #define _SYS_MONITOR_H #include <pdh.h> class SysMonitor: { public: SysMonitor(); ~SysMonitor() {}; float GetUsedCpuPercent(); float GetUsedPhyMemPercent(); int GetUsedPhyMemB(); int GetUsedPhyMemK(); int GetUsedPhyMemM(); int GetFreePhyMemB(); int GetFreePhyMemK(); int GetFreePhyMemM(); private: PDH_HQUERY cpuQuery; PDH_HCOUNTER cpuTotal; }; #endif //SysMonitor.cpp #include "SysMonitor.h" SysMonitor::SysMonitor() { PdhOpenQuery(NULL, NULL, &cpuQuery); PdhAddCounter(cpuQuery, TEXT("\\Processor(_Total)\\% Processor Time"), NULL, &cpuTotal); PdhCollectQueryData(cpuQuery); } float SysMonitor::GetUsedCpuPercent() { PDH_FMT_COUNTERVALUE counterVal; PdhCollectQueryData(cpuQuery); PdhGetFormattedCounterValue(cpuTotal, PDH_FMT_DOUBLE, NULL, &counterVal); return counterVal.doubleValue; } int SysMonitor::GetUsedPhyMemB() { MEMORYSTATUSEX memInfo; memInfo.dwLength = sizeof(MEMORYSTATUSEX); GlobalMemoryStatusEx(&memInfo); return memInfo.ullTotalPhys - memInfo.ullAvailPhys ; } int SysMonitor::GetUsedPhyMemK() { int bytes = GetUsedPhyMemB(); return bytes/1024; } int SysMonitor::GetUsedPhyMemM() { int bytes = GetUsedPhyMemB(); return bytes/(1024*1024); } int SysMonitor::GetFreePhyMemB() { MEMORYSTATUSEX memInfo; memInfo.dwLength = sizeof(MEMORYSTATUSEX); GlobalMemoryStatusEx(&memInfo); return memInfo.ullAvailPhys; } int SysMonitor::GetFreePhyMemK() { int bytes = GetFreePhyMemB(); return bytes/1024; } int SysMonitor::GetFreePhyMemM() { int bytes = GetFreePhyMemB(); return bytes/(1024*1024); } float SysMonitor::GetUsedPhyMemPercent() { MEMORYSTATUSEX memInfo; memInfo.dwLength = sizeof(MEMORYSTATUSEX); GlobalMemoryStatusEx(&memInfo); // cout << "totalVirtualMem = " << memInfo.ullTotalPageFile << endl; // cout << "virtualMemUsed = " << memInfo.ullTotalPageFile - memInfo.ullAvailPageFile << endl; // cout << "totalPhysMem = " << memInfo.ullTotalPhys << endl; // cout << "physMemUsed = " << memInfo.ullTotalPhys - memInfo.ullAvailPhys << endl; return float(memInfo.ullTotalPhys-memInfo.ullAvailPhys)/memInfo.ullTotalPhys*100; }