写一个程序,让用户决定windows任务管理器(Task Manager)的CPU占用率。程序越精简越好,计算语言不限。例如,可以实现下面三种情况
1. CPU的占用率固定在50%,为一条直线;
2. CPU的占用率为一条直线,具体占用率由命令行参数决定(参数范围1~100);
3. CPU的占用率状态是一条正弦曲线
CPU执行应用的程序的时间和刷新周期总时间的比率,就是CPU使用率
要想CPU的使用率控制在50%(或者其他任意比例),我们首先要明白怎么让系统忙起来和闲起来。当一个程序运行的时候,系统就忙起来了。那怎么让它闲下来?很简单,当这些程序在等待用户输入,或者其他操作的时候,或者进入“休眠”的时候。
要操纵CPU的使用率曲线,就需要使CPU在一段时间内(根据任务管理器(Task Manager)的采样率)跑busy和idle两个不同的循环,从而通过不同的时间比率,来调节CPU的使用率
busy : 我们使用一个for循环
for(int i = 0; i < n; ++i)
idle:我们使用Sleep函数
例如: 我们让程序停止10ms
Sleep(10)
这里重要的是知道n的值和需要程序休眠多少时间
CPU频率,就是CPU的时钟频率,简单说是CPU运算时的工作频率(1秒内发生的同步脉冲数)的简称
代码运行的CPU是P4 2.4GHZ 即 2.4 * 10^9
由于现代CPU每个时钟周期可以执行两条以上的代码,于是有
n =(2400000000 * 2)/ 5 = 960000000(循环秒)
和
Sleep(1000)
也就是说CPU1秒钟可以运行这个空循环960000000次
当然由于实际原因,会进行等比例缩放
所以取
n = 9600000
和
Sleep(10)
因为本人的不是单核CPU,所以加了
SetThreadAffinityMask(GetCurrentThread(), 0x00000001);
目的是使这段代码运行在一个CPU里
#include
#include
#include
using namespace std;
int main()
{
SetThreadAffinityMask(GetCurrentThread(), 0x00000001);
for(;;) // while(1)
{
for(int i = 0; i < 9600000; i++)
;
Sleep(10); // 本人电脑上这里要设置成Sleep(20)才能大致趋于50%
}
return 0;
}
上图具体如下
#include
#include
#include
int main()
{
SetThreadAffinityMask(GetCurrentThread(), 0x00000001);
int busyTime = 50;
int idleTime = busyTime;
for(;;) // while(1)
{
int startTime = GetTickCount();
while((GetTickCount() - startTime) < busyTime)
;
Sleep(idleTime);
}
return 0;
}
在上面两种代码中都是假设目前没有其他程序运行,但实际上,我们往往会有很多进程,他们占用了或多或少的CPU,所以上面图中没有出现一条直线
目前还不会C++的写法(可能会有某个API来获取当前的CPU使用率?performanceCounter?不过好像很麻烦),书上给出了C#的自适应解法,这里照搬下来
static void MakeUsage(float level)
{
PerformanceCounter p = new PerformanceCounter("Processor",
"%Processor Time", "__Total");
while(true) {
if(p.NextValue() > level) {
System.Threading.Thread.Sleep(10);
}
}
}
在系统中,CPU不可能真的画出正弦函数,而是有一个个时间点构成的连线,因此我在在正弦函数的一个周期中选择了200个坐标点,用于构成正弦函数图。
在正弦函数的选择上,我使用了最简单的
y=sin(x)
根据数据公式可以简单的知道:
1.周期
T = 2 * PI
2.点与点之间的时间间隔
t = T / 200 // T为周期,200为点的总数
3.busy和idle的时间
busy : busyTime[i] = sin(t * i)
idle:1 - busyTime[i]
// i 表示第几个点
因此得出了下面的代码
#include
#include
#include
const int SAMPLING_COUNT = 200; // 抽样点数量
const double PI = 3.1415926535; // pi值
const int TOTAL_AMPLITUDE = 300; // 每个抽样点对应的时间片
int main()
{
SetThreadAffinityMask(GetCurrentThread(), 0x00000001);
double busyTime[SAMPLING_COUNT];
double radian = 0.0;
double radianIncrement = 2 * PI / SAMPLING_COUNT;
for(int i = 0; i < SAMPLING_COUNT; i++) {
busyTime[i] = sin(radianIncrement * i);
radian += radianIncrement;
}
for(int j = 0; ; j = (j + 1) % SAMPLING_COUNT) {
int startTime = GetTickCount();
while((int)(GetTickCount() - startTime) < busyTime[j])
;
Sleep(1 - busyTime[j]);
}
return 0;
}
图的效果不是很好,因此把代码改为如下
#include
#include
#include
const int SAMPLING_COUNT = 200; // 抽样点数量
const double PI = 3.1415926535; // pi值
const int TOTAL_AMPLITUDE = 300; // 每个抽样点对应的时间片
int main()
{
SetThreadAffinityMask(GetCurrentThread(), 0x00000001);
double busyTime[SAMPLING_COUNT];
double radian = 0.0;
double radianIncrement = 2 * PI / SAMPLING_COUNT;
int amplitude = TOTAL_AMPLITUDE / 2;
for(int i = 0; i < SAMPLING_COUNT; i++) {
busyTime[i] = amplitude + sin(radianIncrement * i) * amplitude;
radian += radianIncrement;
}
for(int j = 0; ; j = (j + 1) % SAMPLING_COUNT) {
int startTime = GetTickCount();
while((int)(GetTickCount() - startTime) < busyTime[j])
;
Sleep(TOTAL_AMPLITUDE - busyTime[j]);
}
return 0;
}
看起来好多了
至此,上面的题已经完成了
代码如下:
#include
#include
#include
int main()
{
SetThreadAffinityMask(GetCurrentThread(), 0x00000001);
int SAMPLING_COUNT = 200;
double TOTAL_AMPLITUDE = 200;
double busyTime[SAMPLING_COUNT];
for(int i = 0; i < SAMPLING_COUNT; i++) {
busyTime[i] = SAMPLING_COUNT / TOTAL_AMPLITUDE * i;
}
for(int j = 0; ; j = (j + 1) % SAMPLING_COUNT) {
int startTime = GetTickCount();
while((int)(GetTickCount() - startTime) < busyTime[j])
;
Sleep(TOTAL_AMPLITUDE - busyTime[j]);
}
return 0;
}