分析与解法:
看一个简单代码:
while(true)
{
if(busy)
i++;
else
}
怎么样才能让电脑不做事情呢?
当任务管理器报告cpu使用率为0的时候,谁使用cpu?通过任务管理器可以看到,system idle process占用了cpu空闲的时间,操作系统中指出,当程序在等待用户的输入,或者是等待某些事情的发生,如WaitForSingleObject(),或者主动进入休眠状态(Sleep()),cpu的使用状态就会为0.
在一个刷新周期内(一般为1s),cpu忙的时间和刷新周期总时间的比率,就是cpu的占用率,也就是说,任务管理器中显示的是每个刷新周期内cpu占用率的统计平均值,所以写一个程序,让它在刷新周期内一会儿忙,一会儿闲,调节忙、闲的比例,就可以控制任务管理器显示cpu占用率
解法一:简单解法:
可以用死循环来控制忙的时间,用sleep()让cpu空闲
如何控制循环的时间?由汇编入手:
next:
mov eax,i
add eax,1
mov i,eax
cmp i,n
jl next
所以总共需要5条指令。
查看电脑,cpu的主频是3.2Ghz,所以每秒有3.2*10的9次方个时钟周期,而每个时钟周期可以执行两条以上的代码,假设取两条,那么每秒可以执行1280000000个循环,如果简单的将n设为1280000000,然后Sleep(1000)是绝对不行的,波形很有可能是锯齿状——先到达一个峰值,然后跌到一个很低的占用率,原因是因为在刷新周期内要忙忙,要忙闲,这决定是不行的,如图:
可以降低两个数量级,在10ms内统计,那么在刷新周期内就是50%的比率了,代码如下:
#include<stdio.h>
#include<windows.h>
#include<stdlib.h>
int main()
{
int i = 0;
for(;;)
{
for(i = 0;i < 5500000;i++);
Sleep(10);
}
return 0;
}
因为系统有其他程序在运行,不断调整n的取值,发现5500000最合适,结果如图:
此方法有一个很大的缺点:不能适应机器差异性,一旦换一个cpu,又必须重新计算n取值,解法二能够动态了解cpu的运算能力,然后自动调节忙/闲时间
解法二:使用GetTickCount()和Sleep()
GetTickCount:系统启动到现在所经历时间的毫秒值
代码:
#include<iostream>
#include<stdio.h>
#include<windows.h>
#include<stdlib.h>
using namespace std;
const int busyTime = 10;
const int idleTime = busyTime*3;
int startTime = 0;
int main()
{
while(true)
{
int startTime = GetTickCount();
//busy loop
while((GetTickCount() - startTime) <= busyTime);
Sleep(idleTime);
}
}
代码中之所以让idleTime = busyTime*3是因为操作系统中有很多程序会同时执行各种各样的任务,如果其他进程占用了10%的cpu,那么我们的程序只能使用40%的cpu
经过调整,让程序的运行时间少一点,与其他程序的运行时间算在一起,差不多50%的利用率。
有没有办法能动态适应?系统监视器,因为要用到c# 跳过 直接看解法4,正弦曲线。
解法四:正弦曲线
代码:
#include<iostream>
#include<stdio.h>
#include<windows.h>
#include<stdlib.h>
#include<math.h>
using namespace std;
//正弦函数为150+150*sin(x)
//把正弦曲线0-T之间的弧度分为200分进行抽样,计算每个抽样点的振幅
//然后每隔300ms的时间取下一个抽样点,并让cpu工作振幅所对应的时间
const int SAMPLING_COUNT = 200;
const double PI = 3.1415926535;
const int TOTAL_AMPLITUDE = 400;//每个抽样点对应的时间片 取300*2差不多在一个刷新周期内
int main()
{
int busySpan[SAMPLING_COUNT];
int amplitude = TOTAL_AMPLITUDE/2;
double radian = 0.0;//弧度
double radianIncrement = 0.01;//弧度增量
for(int i = 0;i < SAMPLING_COUNT;i++)
{
busySpan[i] = amplitude + sin(PI*radian)*amplitude;
radian += radianIncrement;
printf("%d\t%d\t",busySpan[i],TOTAL_AMPLITUDE-busySpan[i]);
}
int startTime = 0;
//循环
for(int j = 0;;j = (j + 1) % SAMPLING_COUNT)
{
startTime = GetTickCount();
while( (GetTickCount() - startTime) <= busySpan[j]);
Sleep((TOTAL_AMPLITUDE-busySpan[j]));
}
return 0;
}
曲线:
讨论: