让CPU占用率曲线听你指挥
2015-12-24 10:04
555 查看
由于网上已经有很多有关此问题的博客,本文参考了http://blog.csdn.net/wesweeky/article/details/6402564
题目:写一个程序,让用户来决定Windows任务管理器(Task Manager)的CPU占用率。程序越精简越好,计算机语言不限。例如,可以实现下面三种情况:
1. CPU的占用率固定在50%,为一条直线;
2. CPU的占用率为一条直线,但是具体占用率由命令行参数决定(参数范围1~ 100);
3. CPU的占用率状态是一个正弦曲线。
控制的方式其实就是在任务管理器刷新的一个周期内,使得cpu忙的时间控制在一定的比例。
任务管理器的刷新周期是1秒,我的cpu是2.4GHZ,
现代cpu每个时钟周期可执行两条以上的代码,取平均值2,于是
(2400 000 000*2)/5=960000000(循环/秒) 每秒可以执行循环960000000次
不能简单的取n=9600000000然后sleep(1000),如果让cpu工作1s,休息1s很可能是锯齿,先达到一个峰值然后跌入一个很低的占有率;所以我们睡眠时间改为10ms,10ms比较接近windows的调度时间,n=9600000。如果sleep时间选的太小,会造成线程频繁的唤醒和挂起,无形中增加了内核时间的不确定性因此代码如下:
一、单核情况下
解法一:
#include <windows.h>
int main(void)
{
//50%
//Thread 0 can only run on CPU 0.
//SetThreadAffinityMask(GetCurrentProcess(), 0x00000001); //这个是指定在哪个cpu上运行该线程
while(true)
{
for(int i=0;i<10120000;i++)
;
Sleep(10);
}
return 0;
}
注意:不同型号的机器的差异性,使得该方法有很大的缺陷,比如我的电脑,只能在调节Sleep(30)的时候才能达到勉强的效果如图:
解法二:
解法二:使用GetTickCount()和Sleep()
GetTickCount()可以得到“系统启动到现在”所经历的时间的毫秒值,最多可以统计49.7天,因此我们可以利用GetTickCount()判断busy loop要循环多久,如下:
[cpp] view
plaincopy
#include <windows.h>
int main(void)
{
//50%
int busyTime=10;
int idleTime=busyTime;
_int64 startTime;
//SetThreadAffinityMask(GetCurrentProcess(), 0x00000001);
while(true)
{
startTime=GetTickCount();
while((GetTickCount()-startTime)<=busyTime)
{
;
}
Sleep(idleTime);
}
return 0;
}
注意:该方法有和方法一有同样的缺点,我的机器的busyTime=50,效果如图:
上面两种解法都是假设当前系统只有当前程序在运行,但实际上,操作系统有很多程序会同时调试执行各种任务,如果此刻进程使用10%的cpu,那我们的程序只有使用40%的cpu才能达到50%的效果。
Perfmon.exe是从WIN NT开始就包含在windows管理工具中的专业检测工具之一。我们可以用程序来查询Perfmon的值,.Net Framework提供了PerformanceCounter这一对象,可以方便的查询当前各种性能数据,包括cpu使用率,因此解法三如下:
解法三:能动态适应的解法
[c-sharp] view
plaincopy
using System;
using System.Diagnostics;
namespace cpu
{
class Program
{
static void Main(string[] args)
{
cpu(0.5);
}
static void cpu(double level)
{
PerformanceCounter p = new PerformanceCounter("Processor", "% Processor Time", "_Total");
if (p == null)
{
return;
}
while (true)
{
if (p.NextValue() > level)
System.Threading.Thread.Sleep(10);
}
}
}
}
由于我的实验都是在虚拟机上安装的Qt开发环境的,没有安装VS,所以无法实验该C#代码的实现。
解法四:正弦曲线
[cpp] view
plaincopy
#include <windows.h>
#include <math.h>
int main(void)
{
SetThreadAffinityMask(GetCurrentProcess(), 0x00000001);
const double SPLIT=0.01;
const int COUNT=200;
const double PI=3.14159265;
const int INTERVAL=300;
DWORD busySpan[COUNT]; //array of busy time
DWORD idleSpan[COUNT]; //array of idle time
int half=INTERVAL/2;
double radian=0.0;
for(int i=0;i<COUNT;i++)
{
busySpan[i]=(DWORD)(half+(sin(PI*radian)*half));
idleSpan[i]=INTERVAL-busySpan[i];
radian+=SPLIT;
}
DWORD startTime=0;
int j=0;
while(true)
{
j=j%COUNT;
startTime=GetTickCount();
while((GetTickCount()-startTime)<=busySpan[j])
;
Sleep(idleSpan[j]);
j++;
}
return 0;
}
其中busySpan[i]=(DWORD)(half+(sin(PI*radian)*half));
idleSpan[i]=INTERVAL-busySpan[i];
这样保证了占有率=busy/(busy+idle)=(half+(sin(PI*radian)*half))/(2*half)=(1+sin(PI*radian))/2 在0到100%之间!。
下面更深入讨论,http://www.cnblogs.com/flyinghearts/archive/2011/03/22/1991965.html这篇博文很好,讨论了在双核情况下,每个cpu显示不同的曲线。如下:
控制CPU占用率,不仅仅是出于好玩而已。以前的某些程序,特别是某些老游戏,在新的机器上运行速度太快,必须先给CPU降速,才能顺利运行那些程序,有个共享软件CPUKiller,就是专门弄这个的。
控制CPU占用率,因为要调用Windows的API,要考虑到多核、超线程的情况,要考虑到不同版本的Windows的计时相关的API的精度不同,使问题变得相当复杂,若再考虑其它程序的CPU占用率,则该问题则变得很烦人。(taskmgr调用了一个未公开的API)。
对CPU核数的判断,书上是调用GetProcessorInfo,其实可以直接调用GetSystemInfo,SYSTEM_INFO结构的dwNumberOfProcessors成员就是核数。不知道超线程对这两种方法有什么影响。
如果不考虑其它程序的CPU占用情况,可以在每个核上开一个线程,运行指定的函数,实现每个核的CPU占用率相同。
要让CPU的占用率,呈函数 y = calc(t) (0 <= y <= 1, t为时间,单位为ms )分布,只要取间隔很短的一系列点,认为在某个间隔内,y值近似不变。
设间隔值为GAP,显然在指定t值附近的GAP这段时间内,
CPU占用时间为:busy = GAP * calc(t),
CPU空闲时间为:idle = GAP - busy
因此,很容易写出下面这个通用函数:
[cpp] view
plaincopy
void solve(Func *calc)
{
double tb = 0;
while(1)
{
unsigned ta = get_time();
double r = calc(tb);
if (r < 0 || r > 1) r = 1;
DWORD busy = r * GAP;
while(get_time() - ta < busy) {}
Sleep(GAP - busy);
tb += GAP;
}
}
如果CPU占用率曲线不是周期性变化,就要对每个t值都要计算一次,否则,可以只计算第一个周期内的各个t值,其它周期的直接取缓存计算结果。
以CPU占用率为正弦曲线为例,显然:y = 0.5 * (1 + sin(a * t + b))
其周期T = 2 * PI / a (PI = 3.1415927),可以指定T值为60s即60000ms,则
可以确定a值为 2 * PI / T, 若在这60000ms内我们计算200次(c = 200),则GAP值为 T / c = 300ms.也就是说,只要确定了周期和计算次数,其它几个参数也都确定下来。
代码如下
[cpp] view
plaincopy
#include<iostream>
#include<cmath>
#include<windows.h>
static int PERIOD = 60 * 1000; //周期ms
const int COUNT = 300; //一个周期计算次数
const double GAP_LINEAR = 100; //线性函数时间间隔100ms
const double PI = 3.1415926535898; //PI
const double GAP = (double)PERIOD / COUNT; //周期函数时间间隔
const double FACTOR = 2 * PI / PERIOD; //周期函数的系数
static double Ratio = 0.5; //线性函数的值 0.5即50%
static double Max=0.9; //方波函数的最大值
static double Min=0.1; //方波函数的最小值
typedef double Func(double); //定义一个函数类型 Func*为函数指针
typedef void Solve(Func *calc);//定义函数类型,参数为函数指针Func*
inline DWORD get_time()
{
return GetTickCount(); //操作系统启动到现在所经过的时间ms
}
double calc_sin(double x) //调用周期函数solve_period的参数
{
return (1 + sin(FACTOR * x)) / 2; //y=1/2(1+sin(a*x))
}
double calc_fangbo(double x) //调用周期函数solve_period的参数
{
//方波函数
if(x<=PERIOD/2) return Max;
else return Min;
}
void solve_period(Func *calc) //线程函数为周期函数
{
double x = 0.0;
double cache[COUNT];
for (int i = 0; i < COUNT; ++i, x += GAP)
cache[i] = calc(x);
int count = 0;
while(1)
{
unsigned ta = get_time();
if (count >= COUNT) count = 0;
double r = cache[count++];
DWORD busy = r * GAP;
while(get_time() - ta < busy) {}
Sleep(GAP - busy);
}
}
void solve_linear(Func*) //线程函数为线性函数,参数为空 NULL
{
const unsigned BUSY = Ratio * GAP_LINEAR;
const unsigned IDLE = (1 - Ratio) * GAP_LINEAR;
while(1)
{
unsigned ta = get_time();
while(get_time() - ta < BUSY) {}
Sleep(IDLE);
}
}
//void solve_nonperiod(Func *calc) //非周期函数的处理,暂没实验
//{
// double tb = 0;
// while(1)
// {
// unsigned ta = get_time();
// double r = calc(tb);
// if (r < 0 || r > 1) r = 1;
// DWORD busy = r * GAP;
// while(get_time() - ta < busy) {}
// Sleep(GAP - busy);
// //tb += GAP;
// tb += get_time() - ta;
// }
//}
void run(int i=1,double R=0.5,double T=60000,double max=0.9,double min=0.1)
//i为输出状态,R为直线函数的值,T为周期函数的周期,max方波最大值,min方波最小值
{
Ratio=R; PERIOD=T; Max=max; Min=min;
Func *func[] = {NULL ,calc_sin,calc_fangbo}; //传给Solve的参数,函数指针数组
Solve *solve_func[] = { solve_linear, solve_period}; //Solve函数指针数组
const int NUM_CPUS = 2; //双核,通用的可以用下面GetSystemInfo得到cpu数目
HANDLE handle[NUM_CPUS];
DWORD thread_id[NUM_CPUS]; //线程id
//SYSTEM_INFO info;
//GetSystemInfo(&info); //得到cpu数目
//const int num = info.dwNumberOfProcessors;
switch(i)
{
case 1: //cpu1 ,cpu2都输出直线
{
for (int i = 0; i < NUM_CPUS; ++i)
 
31e7f
; {
Func *calc = func[0];
Solve *solve = solve_func[0];
if ((handle[i] = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)solve,
(VOID*)calc, 0, &thread_id[i])) != NULL) //创建新线程
SetThreadAffinityMask(handle[i], i+1); //限定线程运行在哪个cpu上
}
WaitForSingleObject(handle[0],INFINITE); //等待线程结束
break;
}
case 2: //cpu1直线,cpu2正弦
{
for (int i = 0; i < NUM_CPUS; ++i)
{
Func *calc = func[i];
Solve *solve = solve_func[i];
if ((handle[i] = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)solve,
(VOID*)calc, 0, &thread_id[i])) != NULL) //创建新线程
SetThreadAffinityMask(handle[i], i+1); //限定线程运行在哪个cpu上
}
WaitForSingleObject(handle[0],INFINITE); //等待线程结束
break;
}
case 3: //cpu1直线,cpu2方波
{
/*Func *calc = func[0];
Solve *solve = solve_func[0];*/
if ((handle[0] = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)solve_func[0],
(VOID*)func[0], 0, &thread_id[0])) != NULL) //创建新线程
SetThreadAffinityMask(handle[0], 1); //限定线程运行在哪个cpu上
Func *calc = func[2];
Solve *solve = solve_func[1];
if ((handle[1] = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)solve,
(VOID*)calc, 0, &thread_id[1])) != NULL) //创建新线程
SetThreadAffinityMask(handle[1], 2); //限定线程运行在哪个cpu上
WaitForSingleObject(handle[0],INFINITE); //等待线程结束
break;
}
case 4: //cpu1正弦,cpu2方波
{
if ((handle[0] = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)solve_func[1],
(VOID*)func[1], 0, &thread_id[0])) != NULL) //创建新线程
SetThreadAffinityMask(handle[0], 1); //限定线程运行在哪个cpu上
Func *calc = func[2];
Solve *solve = solve_func[1];
if ((handle[1] = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)solve,
(VOID*)calc, 0, &thread_id[1])) != NULL) //创建新线程
SetThreadAffinityMask(handle[1], 2); //限定线程运行在哪个cpu上
WaitForSingleObject(handle[0],INFINITE); //等待线程结束
break;
}
default: break;
}
}
void main()
{
run(1,0.5); //cpu1 ,cpu2都输出50%的直线
//run(2,0.5,30000); //cpu1 0.5直线,cpu2正弦周期30000
//run(3); //cpu1直线,cpu2方波
//run(4,0.8,30000,0.95,0.5); //cpu1正弦,cpu2 0.95-0.5的方波
}
下面更深入讨论,http://www.cnblogs.com/flyinghearts/archive/2011/03/22/1991965.html这篇博文很好,讨论了在双核情况下,每个cpu显示不同的曲线。如下:
控制CPU占用率,不仅仅是出于好玩而已。以前的某些程序,特别是某些老游戏,在新的机器上运行速度太快,必须先给CPU降速,才能顺利运行那些程序,有个共享软件CPUKiller,就是专门弄这个的。
控制CPU占用率,因为要调用Windows的API,要考虑到多核、超线程的情况,要考虑到不同版本的Windows的计时相关的API的精度不同,使问题变得相当复杂,若再考虑其它程序的CPU占用率,则该问题则变得很烦人。(taskmgr调用了一个未公开的API)。
对CPU核数的判断,书上是调用GetProcessorInfo,其实可以直接调用GetSystemInfo,SYSTEM_INFO结构的dwNumberOfProcessors成员就是核数。不知道超线程对这两种方法有什么影响。
如果不考虑其它程序的CPU占用情况,可以在每个核上开一个线程,运行指定的函数,实现每个核的CPU占用率相同。
要让CPU的占用率,呈函数 y = calc(t) (0 <= y <= 1, t为时间,单位为ms )分布,只要取间隔很短的一系列点,认为在某个间隔内,y值近似不变。
设间隔值为GAP,显然在指定t值附近的GAP这段时间内,
CPU占用时间为:busy = GAP * calc(t),
CPU空闲时间为:idle = GAP - busy
因此,很容易写出下面这个通用函数:
[cpp] view
plaincopy
void solve(Func *calc)
{
double tb = 0;
while(1)
{
unsigned ta = get_time();
double r = calc(tb);
if (r < 0 || r > 1) r = 1;
DWORD busy = r * GAP;
while(get_time() - ta < busy) {}
Sleep(GAP - busy);
tb += GAP;
}
}
如果CPU占用率曲线不是周期性变化,就要对每个t值都要计算一次,否则,可以只计算第一个周期内的各个t值,其它周期的直接取缓存计算结果。
以CPU占用率为正弦曲线为例,显然:y = 0.5 * (1 + sin(a * t + b))
其周期T = 2 * PI / a (PI = 3.1415927),可以指定T值为60s即60000ms,则
可以确定a值为 2 * PI / T, 若在这60000ms内我们计算200次(c = 200),则GAP值为 T / c = 300ms.也就是说,只要确定了周期和计算次数,其它几个参数也都确定下来。
代码如下
[cpp] view
plaincopy
#include<iostream>
#include<cmath>
#include<windows.h>
static int PERIOD = 60 * 1000; //周期ms
const int COUNT = 300; //一个周期计算次数
const double GAP_LINEAR = 100; //线性函数时间间隔100ms
const double PI = 3.1415926535898; //PI
const double GAP = (double)PERIOD / COUNT; //周期函数时间间隔
const double FACTOR = 2 * PI / PERIOD; //周期函数的系数
static double Ratio = 0.5; //线性函数的值 0.5即50%
static double Max=0.9; //方波函数的最大值
static double Min=0.1; //方波函数的最小值
typedef double Func(double); //定义一个函数类型 Func*为函数指针
typedef void Solve(Func *calc);//定义函数类型,参数为函数指针Func*
inline DWORD get_time()
{
return GetTickCount(); //操作系统启动到现在所经过的时间ms
}
double calc_sin(double x) //调用周期函数solve_period的参数
{
return (1 + sin(FACTOR * x)) / 2; //y=1/2(1+sin(a*x))
}
double calc_fangbo(double x) //调用周期函数solve_period的参数
{
//方波函数
if(x<=PERIOD/2) return Max;
else return Min;
}
void solve_period(Func *calc) //线程函数为周期函数
{
double x = 0.0;
double cache[COUNT];
for (int i = 0; i < COUNT; ++i, x += GAP)
cache[i] = calc(x);
int count = 0;
while(1)
{
unsigned ta = get_time();
if (count >= COUNT) count = 0;
double r = cache[count++];
DWORD busy = r * GAP;
while(get_time() - ta < busy) {}
Sleep(GAP - busy);
}
}
void solve_linear(Func*) //线程函数为线性函数,参数为空 NULL
{
const unsigned BUSY = Ratio * GAP_LINEAR;
const unsigned IDLE = (1 - Ratio) * GAP_LINEAR;
while(1)
{
unsigned ta = get_time();
while(get_time() - ta < BUSY) {}
Sleep(IDLE);
}
}
//void solve_nonperiod(Func *calc) //非周期函数的处理,暂没实验
//{
// double tb = 0;
// while(1)
// {
// unsigned ta = get_time();
// double r = calc(tb);
// if (r < 0 || r > 1) r = 1;
// DWORD busy = r * GAP;
// while(get_time() - ta < busy) {}
// Sleep(GAP - busy);
// //tb += GAP;
// tb += get_time() - ta;
// }
//}
void run(int i=1,double R=0.5,double T=60000,double max=0.9,double min=0.1)
//i为输出状态,R为直线函数的值,T为周期函数的周期,max方波最大值,min方波最小值
{
Ratio=R; PERIOD=T; Max=max; Min=min;
Func *func[] = {NULL ,calc_sin,calc_fangbo}; //传给Solve的参数,函数指针数组
Solve *solve_func[] = { solve_linear, solve_period}; //Solve函数指针数组
const int NUM_CPUS = 2; //双核,通用的可以用下面GetSystemInfo得到cpu数目
HANDLE handle[NUM_CPUS];
DWORD thread_id[NUM_CPUS]; //线程id
//SYSTEM_INFO info;
//GetSystemInfo(&info); //得到cpu数目
//const int num = info.dwNumberOfProcessors;
switch(i)
{
case 1: //cpu1 ,cpu2都输出直线
{
for (int i = 0; i < NUM_CPUS; ++i)
{
Func *calc = func[0];
Solve *solve = solve_func[0];
if ((handle[i] = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)solve,
(VOID*)calc, 0, &thread_id[i])) != NULL) //创建新线程
SetThreadAffinityMask(handle[i], i+1); //限定线程运行在哪个cpu上
}
WaitForSingleObject(handle[0],INFINITE); //等待线程结束
break;
}
case 2: //cpu1直线,cpu2正弦
{
for (int i = 0; i < NUM_CPUS; ++i)
{
Func *calc = func[i];
Solve *solve = solve_func[i];
if ((handle[i] = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)solve,
(VOID*)calc, 0, &thread_id[i])) != NULL) //创建新线程
SetThreadAffinityMask(handle[i], i+1); //限定线程运行在哪个cpu上
}
WaitForSingleObject(handle[0],INFINITE); //等待线程结束
break;
}
case 3: //cpu1直线,cpu2方波
{
/*Func *calc = func[0];
Solve *solve = solve_func[0];*/
if ((handle[0] = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)solve_func[0],
(VOID*)func[0], 0, &thread_id[0])) != NULL) //创建新线程
SetThreadAffinityMask(handle[0], 1); //限定线程运行在哪个cpu上
Func *calc = func[2];
Solve *solve = solve_func[1];
if ((handle[1] = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)solve,
(VOID*)calc, 0, &thread_id[1])) != NULL) //创建新线程
SetThreadAffinityMask(handle[1], 2); //限定线程运行在哪个cpu上
WaitForSingleObject(handle[0],INFINITE); //等待线程结束
break;
}
case 4: //cpu1正弦,cpu2方波
{
if ((handle[0] = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)solve_func[1],
(VOID*)func[1], 0, &thread_id[0])) != NULL) //创建新线程
SetThreadAffinityMask(handle[0], 1); //限定线程运行在哪个cpu上
Func *calc = func[2];
Solve *solve = solve_func[1];
if ((handle[1] = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)solve,
(VOID*)calc, 0, &thread_id[1])) != NULL) //创建新线程
SetThreadAffinityMask(handle[1], 2); //限定线程运行在哪个cpu上
WaitForSingleObject(handle[0],INFINITE); //等待线程结束
break;
}
default: break;
}
}
void main()
{
run(1,0.5); //cpu1 ,cpu2都输出50%的直线
//run(2,0.5,30000); //cpu1 0.5直线,cpu2正弦周期30000
//run(3); //cpu1直线,cpu2方波
//run(4,0.8,30000,0.95,0.5); //cpu1正弦,cpu2 0.95-0.5的方波
}
效果如图:
补充:我本人的电脑是Win10系统 i7-4700八核处理器,无奈不知道为什么使用以上方法很难实现,以上的效果都是我用虚拟机弄的winXP实现的。
比如当我用上面的多核的方法的时候,我把处理器核数改为8会出现如下的效果:
发现前四个处理器满载,其余也有不定占有率,我怀疑是不是这是win10的负载平衡导致的。
题目:写一个程序,让用户来决定Windows任务管理器(Task Manager)的CPU占用率。程序越精简越好,计算机语言不限。例如,可以实现下面三种情况:
1. CPU的占用率固定在50%,为一条直线;
2. CPU的占用率为一条直线,但是具体占用率由命令行参数决定(参数范围1~ 100);
3. CPU的占用率状态是一个正弦曲线。
控制的方式其实就是在任务管理器刷新的一个周期内,使得cpu忙的时间控制在一定的比例。
任务管理器的刷新周期是1秒,我的cpu是2.4GHZ,
现代cpu每个时钟周期可执行两条以上的代码,取平均值2,于是
(2400 000 000*2)/5=960000000(循环/秒) 每秒可以执行循环960000000次
不能简单的取n=9600000000然后sleep(1000),如果让cpu工作1s,休息1s很可能是锯齿,先达到一个峰值然后跌入一个很低的占有率;所以我们睡眠时间改为10ms,10ms比较接近windows的调度时间,n=9600000。如果sleep时间选的太小,会造成线程频繁的唤醒和挂起,无形中增加了内核时间的不确定性因此代码如下:
一、单核情况下
解法一:
#include <windows.h>
int main(void)
{
//50%
//Thread 0 can only run on CPU 0.
//SetThreadAffinityMask(GetCurrentProcess(), 0x00000001); //这个是指定在哪个cpu上运行该线程
while(true)
{
for(int i=0;i<10120000;i++)
;
Sleep(10);
}
return 0;
}
注意:不同型号的机器的差异性,使得该方法有很大的缺陷,比如我的电脑,只能在调节Sleep(30)的时候才能达到勉强的效果如图:
解法二:
解法二:使用GetTickCount()和Sleep()
GetTickCount()可以得到“系统启动到现在”所经历的时间的毫秒值,最多可以统计49.7天,因此我们可以利用GetTickCount()判断busy loop要循环多久,如下:
[cpp] view
plaincopy
#include <windows.h>
int main(void)
{
//50%
int busyTime=10;
int idleTime=busyTime;
_int64 startTime;
//SetThreadAffinityMask(GetCurrentProcess(), 0x00000001);
while(true)
{
startTime=GetTickCount();
while((GetTickCount()-startTime)<=busyTime)
{
;
}
Sleep(idleTime);
}
return 0;
}
注意:该方法有和方法一有同样的缺点,我的机器的busyTime=50,效果如图:
上面两种解法都是假设当前系统只有当前程序在运行,但实际上,操作系统有很多程序会同时调试执行各种任务,如果此刻进程使用10%的cpu,那我们的程序只有使用40%的cpu才能达到50%的效果。
Perfmon.exe是从WIN NT开始就包含在windows管理工具中的专业检测工具之一。我们可以用程序来查询Perfmon的值,.Net Framework提供了PerformanceCounter这一对象,可以方便的查询当前各种性能数据,包括cpu使用率,因此解法三如下:
解法三:能动态适应的解法
[c-sharp] view
plaincopy
using System;
using System.Diagnostics;
namespace cpu
{
class Program
{
static void Main(string[] args)
{
cpu(0.5);
}
static void cpu(double level)
{
PerformanceCounter p = new PerformanceCounter("Processor", "% Processor Time", "_Total");
if (p == null)
{
return;
}
while (true)
{
if (p.NextValue() > level)
System.Threading.Thread.Sleep(10);
}
}
}
}
由于我的实验都是在虚拟机上安装的Qt开发环境的,没有安装VS,所以无法实验该C#代码的实现。
解法四:正弦曲线
[cpp] view
plaincopy
#include <windows.h>
#include <math.h>
int main(void)
{
SetThreadAffinityMask(GetCurrentProcess(), 0x00000001);
const double SPLIT=0.01;
const int COUNT=200;
const double PI=3.14159265;
const int INTERVAL=300;
DWORD busySpan[COUNT]; //array of busy time
DWORD idleSpan[COUNT]; //array of idle time
int half=INTERVAL/2;
double radian=0.0;
for(int i=0;i<COUNT;i++)
{
busySpan[i]=(DWORD)(half+(sin(PI*radian)*half));
idleSpan[i]=INTERVAL-busySpan[i];
radian+=SPLIT;
}
DWORD startTime=0;
int j=0;
while(true)
{
j=j%COUNT;
startTime=GetTickCount();
while((GetTickCount()-startTime)<=busySpan[j])
;
Sleep(idleSpan[j]);
j++;
}
return 0;
}
其中busySpan[i]=(DWORD)(half+(sin(PI*radian)*half));
idleSpan[i]=INTERVAL-busySpan[i];
这样保证了占有率=busy/(busy+idle)=(half+(sin(PI*radian)*half))/(2*half)=(1+sin(PI*radian))/2 在0到100%之间!。
下面更深入讨论,http://www.cnblogs.com/flyinghearts/archive/2011/03/22/1991965.html这篇博文很好,讨论了在双核情况下,每个cpu显示不同的曲线。如下:
控制CPU占用率,不仅仅是出于好玩而已。以前的某些程序,特别是某些老游戏,在新的机器上运行速度太快,必须先给CPU降速,才能顺利运行那些程序,有个共享软件CPUKiller,就是专门弄这个的。
控制CPU占用率,因为要调用Windows的API,要考虑到多核、超线程的情况,要考虑到不同版本的Windows的计时相关的API的精度不同,使问题变得相当复杂,若再考虑其它程序的CPU占用率,则该问题则变得很烦人。(taskmgr调用了一个未公开的API)。
对CPU核数的判断,书上是调用GetProcessorInfo,其实可以直接调用GetSystemInfo,SYSTEM_INFO结构的dwNumberOfProcessors成员就是核数。不知道超线程对这两种方法有什么影响。
如果不考虑其它程序的CPU占用情况,可以在每个核上开一个线程,运行指定的函数,实现每个核的CPU占用率相同。
要让CPU的占用率,呈函数 y = calc(t) (0 <= y <= 1, t为时间,单位为ms )分布,只要取间隔很短的一系列点,认为在某个间隔内,y值近似不变。
设间隔值为GAP,显然在指定t值附近的GAP这段时间内,
CPU占用时间为:busy = GAP * calc(t),
CPU空闲时间为:idle = GAP - busy
因此,很容易写出下面这个通用函数:
[cpp] view
plaincopy
void solve(Func *calc)
{
double tb = 0;
while(1)
{
unsigned ta = get_time();
double r = calc(tb);
if (r < 0 || r > 1) r = 1;
DWORD busy = r * GAP;
while(get_time() - ta < busy) {}
Sleep(GAP - busy);
tb += GAP;
}
}
如果CPU占用率曲线不是周期性变化,就要对每个t值都要计算一次,否则,可以只计算第一个周期内的各个t值,其它周期的直接取缓存计算结果。
以CPU占用率为正弦曲线为例,显然:y = 0.5 * (1 + sin(a * t + b))
其周期T = 2 * PI / a (PI = 3.1415927),可以指定T值为60s即60000ms,则
可以确定a值为 2 * PI / T, 若在这60000ms内我们计算200次(c = 200),则GAP值为 T / c = 300ms.也就是说,只要确定了周期和计算次数,其它几个参数也都确定下来。
代码如下
[cpp] view
plaincopy
#include<iostream>
#include<cmath>
#include<windows.h>
static int PERIOD = 60 * 1000; //周期ms
const int COUNT = 300; //一个周期计算次数
const double GAP_LINEAR = 100; //线性函数时间间隔100ms
const double PI = 3.1415926535898; //PI
const double GAP = (double)PERIOD / COUNT; //周期函数时间间隔
const double FACTOR = 2 * PI / PERIOD; //周期函数的系数
static double Ratio = 0.5; //线性函数的值 0.5即50%
static double Max=0.9; //方波函数的最大值
static double Min=0.1; //方波函数的最小值
typedef double Func(double); //定义一个函数类型 Func*为函数指针
typedef void Solve(Func *calc);//定义函数类型,参数为函数指针Func*
inline DWORD get_time()
{
return GetTickCount(); //操作系统启动到现在所经过的时间ms
}
double calc_sin(double x) //调用周期函数solve_period的参数
{
return (1 + sin(FACTOR * x)) / 2; //y=1/2(1+sin(a*x))
}
double calc_fangbo(double x) //调用周期函数solve_period的参数
{
//方波函数
if(x<=PERIOD/2) return Max;
else return Min;
}
void solve_period(Func *calc) //线程函数为周期函数
{
double x = 0.0;
double cache[COUNT];
for (int i = 0; i < COUNT; ++i, x += GAP)
cache[i] = calc(x);
int count = 0;
while(1)
{
unsigned ta = get_time();
if (count >= COUNT) count = 0;
double r = cache[count++];
DWORD busy = r * GAP;
while(get_time() - ta < busy) {}
Sleep(GAP - busy);
}
}
void solve_linear(Func*) //线程函数为线性函数,参数为空 NULL
{
const unsigned BUSY = Ratio * GAP_LINEAR;
const unsigned IDLE = (1 - Ratio) * GAP_LINEAR;
while(1)
{
unsigned ta = get_time();
while(get_time() - ta < BUSY) {}
Sleep(IDLE);
}
}
//void solve_nonperiod(Func *calc) //非周期函数的处理,暂没实验
//{
// double tb = 0;
// while(1)
// {
// unsigned ta = get_time();
// double r = calc(tb);
// if (r < 0 || r > 1) r = 1;
// DWORD busy = r * GAP;
// while(get_time() - ta < busy) {}
// Sleep(GAP - busy);
// //tb += GAP;
// tb += get_time() - ta;
// }
//}
void run(int i=1,double R=0.5,double T=60000,double max=0.9,double min=0.1)
//i为输出状态,R为直线函数的值,T为周期函数的周期,max方波最大值,min方波最小值
{
Ratio=R; PERIOD=T; Max=max; Min=min;
Func *func[] = {NULL ,calc_sin,calc_fangbo}; //传给Solve的参数,函数指针数组
Solve *solve_func[] = { solve_linear, solve_period}; //Solve函数指针数组
const int NUM_CPUS = 2; //双核,通用的可以用下面GetSystemInfo得到cpu数目
HANDLE handle[NUM_CPUS];
DWORD thread_id[NUM_CPUS]; //线程id
//SYSTEM_INFO info;
//GetSystemInfo(&info); //得到cpu数目
//const int num = info.dwNumberOfProcessors;
switch(i)
{
case 1: //cpu1 ,cpu2都输出直线
{
for (int i = 0; i < NUM_CPUS; ++i)
 
31e7f
; {
Func *calc = func[0];
Solve *solve = solve_func[0];
if ((handle[i] = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)solve,
(VOID*)calc, 0, &thread_id[i])) != NULL) //创建新线程
SetThreadAffinityMask(handle[i], i+1); //限定线程运行在哪个cpu上
}
WaitForSingleObject(handle[0],INFINITE); //等待线程结束
break;
}
case 2: //cpu1直线,cpu2正弦
{
for (int i = 0; i < NUM_CPUS; ++i)
{
Func *calc = func[i];
Solve *solve = solve_func[i];
if ((handle[i] = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)solve,
(VOID*)calc, 0, &thread_id[i])) != NULL) //创建新线程
SetThreadAffinityMask(handle[i], i+1); //限定线程运行在哪个cpu上
}
WaitForSingleObject(handle[0],INFINITE); //等待线程结束
break;
}
case 3: //cpu1直线,cpu2方波
{
/*Func *calc = func[0];
Solve *solve = solve_func[0];*/
if ((handle[0] = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)solve_func[0],
(VOID*)func[0], 0, &thread_id[0])) != NULL) //创建新线程
SetThreadAffinityMask(handle[0], 1); //限定线程运行在哪个cpu上
Func *calc = func[2];
Solve *solve = solve_func[1];
if ((handle[1] = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)solve,
(VOID*)calc, 0, &thread_id[1])) != NULL) //创建新线程
SetThreadAffinityMask(handle[1], 2); //限定线程运行在哪个cpu上
WaitForSingleObject(handle[0],INFINITE); //等待线程结束
break;
}
case 4: //cpu1正弦,cpu2方波
{
if ((handle[0] = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)solve_func[1],
(VOID*)func[1], 0, &thread_id[0])) != NULL) //创建新线程
SetThreadAffinityMask(handle[0], 1); //限定线程运行在哪个cpu上
Func *calc = func[2];
Solve *solve = solve_func[1];
if ((handle[1] = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)solve,
(VOID*)calc, 0, &thread_id[1])) != NULL) //创建新线程
SetThreadAffinityMask(handle[1], 2); //限定线程运行在哪个cpu上
WaitForSingleObject(handle[0],INFINITE); //等待线程结束
break;
}
default: break;
}
}
void main()
{
run(1,0.5); //cpu1 ,cpu2都输出50%的直线
//run(2,0.5,30000); //cpu1 0.5直线,cpu2正弦周期30000
//run(3); //cpu1直线,cpu2方波
//run(4,0.8,30000,0.95,0.5); //cpu1正弦,cpu2 0.95-0.5的方波
}
下面更深入讨论,http://www.cnblogs.com/flyinghearts/archive/2011/03/22/1991965.html这篇博文很好,讨论了在双核情况下,每个cpu显示不同的曲线。如下:
控制CPU占用率,不仅仅是出于好玩而已。以前的某些程序,特别是某些老游戏,在新的机器上运行速度太快,必须先给CPU降速,才能顺利运行那些程序,有个共享软件CPUKiller,就是专门弄这个的。
控制CPU占用率,因为要调用Windows的API,要考虑到多核、超线程的情况,要考虑到不同版本的Windows的计时相关的API的精度不同,使问题变得相当复杂,若再考虑其它程序的CPU占用率,则该问题则变得很烦人。(taskmgr调用了一个未公开的API)。
对CPU核数的判断,书上是调用GetProcessorInfo,其实可以直接调用GetSystemInfo,SYSTEM_INFO结构的dwNumberOfProcessors成员就是核数。不知道超线程对这两种方法有什么影响。
如果不考虑其它程序的CPU占用情况,可以在每个核上开一个线程,运行指定的函数,实现每个核的CPU占用率相同。
要让CPU的占用率,呈函数 y = calc(t) (0 <= y <= 1, t为时间,单位为ms )分布,只要取间隔很短的一系列点,认为在某个间隔内,y值近似不变。
设间隔值为GAP,显然在指定t值附近的GAP这段时间内,
CPU占用时间为:busy = GAP * calc(t),
CPU空闲时间为:idle = GAP - busy
因此,很容易写出下面这个通用函数:
[cpp] view
plaincopy
void solve(Func *calc)
{
double tb = 0;
while(1)
{
unsigned ta = get_time();
double r = calc(tb);
if (r < 0 || r > 1) r = 1;
DWORD busy = r * GAP;
while(get_time() - ta < busy) {}
Sleep(GAP - busy);
tb += GAP;
}
}
如果CPU占用率曲线不是周期性变化,就要对每个t值都要计算一次,否则,可以只计算第一个周期内的各个t值,其它周期的直接取缓存计算结果。
以CPU占用率为正弦曲线为例,显然:y = 0.5 * (1 + sin(a * t + b))
其周期T = 2 * PI / a (PI = 3.1415927),可以指定T值为60s即60000ms,则
可以确定a值为 2 * PI / T, 若在这60000ms内我们计算200次(c = 200),则GAP值为 T / c = 300ms.也就是说,只要确定了周期和计算次数,其它几个参数也都确定下来。
代码如下
[cpp] view
plaincopy
#include<iostream>
#include<cmath>
#include<windows.h>
static int PERIOD = 60 * 1000; //周期ms
const int COUNT = 300; //一个周期计算次数
const double GAP_LINEAR = 100; //线性函数时间间隔100ms
const double PI = 3.1415926535898; //PI
const double GAP = (double)PERIOD / COUNT; //周期函数时间间隔
const double FACTOR = 2 * PI / PERIOD; //周期函数的系数
static double Ratio = 0.5; //线性函数的值 0.5即50%
static double Max=0.9; //方波函数的最大值
static double Min=0.1; //方波函数的最小值
typedef double Func(double); //定义一个函数类型 Func*为函数指针
typedef void Solve(Func *calc);//定义函数类型,参数为函数指针Func*
inline DWORD get_time()
{
return GetTickCount(); //操作系统启动到现在所经过的时间ms
}
double calc_sin(double x) //调用周期函数solve_period的参数
{
return (1 + sin(FACTOR * x)) / 2; //y=1/2(1+sin(a*x))
}
double calc_fangbo(double x) //调用周期函数solve_period的参数
{
//方波函数
if(x<=PERIOD/2) return Max;
else return Min;
}
void solve_period(Func *calc) //线程函数为周期函数
{
double x = 0.0;
double cache[COUNT];
for (int i = 0; i < COUNT; ++i, x += GAP)
cache[i] = calc(x);
int count = 0;
while(1)
{
unsigned ta = get_time();
if (count >= COUNT) count = 0;
double r = cache[count++];
DWORD busy = r * GAP;
while(get_time() - ta < busy) {}
Sleep(GAP - busy);
}
}
void solve_linear(Func*) //线程函数为线性函数,参数为空 NULL
{
const unsigned BUSY = Ratio * GAP_LINEAR;
const unsigned IDLE = (1 - Ratio) * GAP_LINEAR;
while(1)
{
unsigned ta = get_time();
while(get_time() - ta < BUSY) {}
Sleep(IDLE);
}
}
//void solve_nonperiod(Func *calc) //非周期函数的处理,暂没实验
//{
// double tb = 0;
// while(1)
// {
// unsigned ta = get_time();
// double r = calc(tb);
// if (r < 0 || r > 1) r = 1;
// DWORD busy = r * GAP;
// while(get_time() - ta < busy) {}
// Sleep(GAP - busy);
// //tb += GAP;
// tb += get_time() - ta;
// }
//}
void run(int i=1,double R=0.5,double T=60000,double max=0.9,double min=0.1)
//i为输出状态,R为直线函数的值,T为周期函数的周期,max方波最大值,min方波最小值
{
Ratio=R; PERIOD=T; Max=max; Min=min;
Func *func[] = {NULL ,calc_sin,calc_fangbo}; //传给Solve的参数,函数指针数组
Solve *solve_func[] = { solve_linear, solve_period}; //Solve函数指针数组
const int NUM_CPUS = 2; //双核,通用的可以用下面GetSystemInfo得到cpu数目
HANDLE handle[NUM_CPUS];
DWORD thread_id[NUM_CPUS]; //线程id
//SYSTEM_INFO info;
//GetSystemInfo(&info); //得到cpu数目
//const int num = info.dwNumberOfProcessors;
switch(i)
{
case 1: //cpu1 ,cpu2都输出直线
{
for (int i = 0; i < NUM_CPUS; ++i)
{
Func *calc = func[0];
Solve *solve = solve_func[0];
if ((handle[i] = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)solve,
(VOID*)calc, 0, &thread_id[i])) != NULL) //创建新线程
SetThreadAffinityMask(handle[i], i+1); //限定线程运行在哪个cpu上
}
WaitForSingleObject(handle[0],INFINITE); //等待线程结束
break;
}
case 2: //cpu1直线,cpu2正弦
{
for (int i = 0; i < NUM_CPUS; ++i)
{
Func *calc = func[i];
Solve *solve = solve_func[i];
if ((handle[i] = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)solve,
(VOID*)calc, 0, &thread_id[i])) != NULL) //创建新线程
SetThreadAffinityMask(handle[i], i+1); //限定线程运行在哪个cpu上
}
WaitForSingleObject(handle[0],INFINITE); //等待线程结束
break;
}
case 3: //cpu1直线,cpu2方波
{
/*Func *calc = func[0];
Solve *solve = solve_func[0];*/
if ((handle[0] = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)solve_func[0],
(VOID*)func[0], 0, &thread_id[0])) != NULL) //创建新线程
SetThreadAffinityMask(handle[0], 1); //限定线程运行在哪个cpu上
Func *calc = func[2];
Solve *solve = solve_func[1];
if ((handle[1] = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)solve,
(VOID*)calc, 0, &thread_id[1])) != NULL) //创建新线程
SetThreadAffinityMask(handle[1], 2); //限定线程运行在哪个cpu上
WaitForSingleObject(handle[0],INFINITE); //等待线程结束
break;
}
case 4: //cpu1正弦,cpu2方波
{
if ((handle[0] = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)solve_func[1],
(VOID*)func[1], 0, &thread_id[0])) != NULL) //创建新线程
SetThreadAffinityMask(handle[0], 1); //限定线程运行在哪个cpu上
Func *calc = func[2];
Solve *solve = solve_func[1];
if ((handle[1] = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)solve,
(VOID*)calc, 0, &thread_id[1])) != NULL) //创建新线程
SetThreadAffinityMask(handle[1], 2); //限定线程运行在哪个cpu上
WaitForSingleObject(handle[0],INFINITE); //等待线程结束
break;
}
default: break;
}
}
void main()
{
run(1,0.5); //cpu1 ,cpu2都输出50%的直线
//run(2,0.5,30000); //cpu1 0.5直线,cpu2正弦周期30000
//run(3); //cpu1直线,cpu2方波
//run(4,0.8,30000,0.95,0.5); //cpu1正弦,cpu2 0.95-0.5的方波
}
效果如图:
补充:我本人的电脑是Win10系统 i7-4700八核处理器,无奈不知道为什么使用以上方法很难实现,以上的效果都是我用虚拟机弄的winXP实现的。
比如当我用上面的多核的方法的时候,我把处理器核数改为8会出现如下的效果:
发现前四个处理器满载,其余也有不定占有率,我怀疑是不是这是win10的负载平衡导致的。
相关文章推荐
- 书评:《算法之美( Algorithms to Live By )》
- 动易2006序列号破解算法公布
- Ruby实现的矩阵连乘算法
- C#插入法排序算法实例分析
- 超大数据量存储常用数据库分表分库算法总结
- C#数据结构与算法揭秘二
- C#冒泡法排序算法实例分析
- 算法练习之从String.indexOf的模拟实现开始
- C#算法之关于大牛生小牛的问题
- C#实现的算24点游戏算法实例分析
- c语言实现的带通配符匹配算法
- 浅析STL中的常用算法
- 算法之排列算法与组合算法详解
- C++实现一维向量旋转算法
- Ruby实现的合并排序算法
- C#折半插入排序算法实现方法
- 基于C++实现的各种内部排序算法汇总
- C++线性时间的排序算法分析
- C++实现汉诺塔算法经典实例
- PHP实现克鲁斯卡尔算法实例解析