问题:
写一个程序。让用户来决定Windows任务管理器(Task Manager)的CPU占用率(单核)。
有下面几种情况:
1.CPU占用率固定在50%,为一条直线
2.CPU的占用率为一条直线,详细占用率由命令行參数决定(范围1~100)
3.CPU的占用率状态为一条正弦曲线
4.多核处理器情况下上述问题怎么解决
分析与解答
首先确定CPU占用率的定义,即在任务管理器的一个刷新周期内。CPU忙(运行应用程序)的时间和刷新周期总时间的比率,就是CPU的占用率。也能够说成,任务管理器中显示的是每一个刷新周期内CPU占用率的统计平均值。所以能够写个程序。在一个刷新周期中。一会儿忙,一会儿闲,调节忙/闲比例,就能够控制CPU占有率了。
一个刷新时间是多久,书上说,通过对任务管理器观測,大约是1秒。鼠标移动、后台程序等都会对曲线造成影响!
单核环境下。空死循环会导致100%的CPU占用率。双核环境下,CPU总占用率大约为50%。四核是25%左右。
解法一:简单解法
Busy用可循环来实现,for(i=0;i<n;i++) ;相应的汇编语言为
loop;
mov dx i ;将i置入dx寄存器
inc dx ;将dx寄存器加1
mov dx i ;将dx中的值赋回i
cmp i n ;比較i和n
j1 loop 。i小于n时则反复循环
我的cpu是 I5 2410M 2.30GHZ(双核四线程,如图) 由于眼下的cpu每一个时钟周期可运行两条以上的代码,取平均值2,于是(2300000000*2)/5=920000000(循环/秒) 每秒能够运行循环920000000次。
不能简单的取n=920000000然后sleep(1000)。假设让cpu工作1s,歇息1s非常可能是锯齿。先达到一个峰值然后跌入一个非常低的占有率。所以我们睡眠时间改为100ms。100ms比較接近windows的调度时间,n=92000000。假设sleep时间选的太小。会造成线程频繁的唤醒和挂起,无形中添加了内核时间的不确定性因此代码例如以下:
#include <windows.h>
int main(void)
{
//Run on CPU 0(0x00000001)(00000001)
//Run on CPU 1(0x00000002)(00000010)
//Run on CPU 0 AND CPU 1(0x00000003)(00000101)
//Run on CPU 2(0x00000004)(00000100)
//......
//SetProcessAffinityMask(GetCurrentProcess(),0x1);//进程与指定cpu绑定
SetThreadAffinityMask(GetCurrentThread(), 0x1);//线程与指定cpu绑定
while(true)
{
for(int i=0;i<92000000;i++)
;
Sleep(100);
}
return 0;
}
使用SetProcessAffinityMask函数。进程与CPU绑定。得到例如以下图。
使用SetThreadAffinityMask函数,进程与CPU绑定,得到例如以下图。
解法二:使用GetTickCount()和Sleep()
GetTickCount()能够得到“系统启动到如今”所经历的时间的毫秒值,最多能够统计49.7天,能够利用GetTickCount()推断循环的时间,代码例如以下:#include <windows.h>
const int busyTime=100;
const int idleTime=busyTime;
int main(void)
{
double startTime;
SetProcessAffinityMask(GetCurrentProcess(), 0x1);
while(true)
{
startTime=GetTickCount();
while((GetTickCount() - startTime) <= busyTime)
{
;
}
Sleep(idleTime);
}
return 0;
}
效果例如以下图所看到的,与第一种
解法效果差点儿相同,由于都如果当前系统仅仅有当前程序在执行,但实际上,操作系统有非常多程序会同一时候调试执行各种任务,如果此刻进程使用20%的cpu。那我们的程序仅仅有使用30%的cpu才干达到50%的效果。
解法三:能动态适应的解法
using System;
using System.Diagnostics;
namespace CPU
{
class Program
{
static void Main(string[] args)
{
cpu(1000);
}
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(1000);
}
}
}
}
解法四:正弦曲线
#include <windows.h>
#include <math.h>
int main(void)
{
SetProcessAffinityMask(GetCurrentProcess(), 0x1);
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;
}
进一步讨论:
控制CPU占用率。由于要调用Windows的API。要考虑到多核、超线程的情况,要考虑到不同版本号的Windows的计时相关的API的精度不同,使问题变得相当复杂,若再考虑其他程序的CPU占用率,则该问题则变得非常烦人。(taskmgr调用了一个未公开的API)。对CPU核数的推断,书上是调用GetProcessorInfo。事实上能够直接调用GetSystemInfo,SYSTEM_INFO结构的dwNumberOfProcessors成员就是核数。假设不考虑其他程序的CPU占用情况,能够在每一个核上开一个线程,执行指定的函数。实现每一个核的CPU占用率同样。假设CPU占用率曲线不是周期性变化,就要对每一个t值都要计算一次,否则,能够仅仅计算第一个周期内的各个t值。其他周期的直接取缓存计算结果。
#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 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函数指针数组
SYSTEM_INFO info;
GetSystemInfo(&info); //得到cpu数目
int NUM_CPUS = info.dwNumberOfProcessors;
HANDLE *handle = new HANDLE[NUM_CPUS];
DWORD *thread_id = new DWORD[NUM_CPUS]; //线程id
switch(i)
{
case 1: //cpu0 ,cpu1都输出直线
{
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); //限定线程执行在哪个cpu上
}
WaitForSingleObject(handle[0],INFINITE); //等待线程结束
break;
}
case 2: //cpu0直线,cpu1正弦
{
if ((handle[1] = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)solve_func[0],
(VOID*)func[0], 0, &thread_id[1])) != NULL) //创建新线程
SetThreadAffinityMask(handle[1], 1); //限定线程执行在哪个cpu上
Func *calc = func[1];
Solve *solve = solve_func[1];
if ((handle[0] = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)solve,
(VOID*)calc, 0, &thread_id[0])) != NULL) //创建新线程
SetThreadAffinityMask(handle[0], 2); //限定线程执行在哪个cpu上
WaitForSingleObject(handle[0],INFINITE); //等待线程结束
break;
}
case 3: //cpu0直线,cpu1方波
{
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: //cpu0正弦。cpu1方波
{
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的方波
}
这是是第一次CPU跑直线。第二个CPU跑正弦函数~~~~~~~~·