讓CPU占用率曲線聽你指揮
寫一個程序,讓用於來決定Windows任務管理器(Task Manager)的CPU占用率。程序越精簡越好,計算機語言不限。例如,可以實現下面三種情況:
- CPU的占用率固定在50%,為一條直線;
- CPU的占用率為一條直線,但是具體占用率由命令行參數決定(參數范圍1~100);
- CPU的占用率狀態是一個正弦曲線。
【解法一】 簡單的解法
一個空的for循環for(int i=0;i<n;i++); 寫成匯編代碼后進行分析:
loop:
mov dx i ;將i置入dx寄存器
inc dx ;將dx從寄存器加1
mov i dx ;將dx中的值賦回i
cmp i n ;比較i和n
jl loop ; i小於n時則重復循環
需要執行5條代碼,假設要運行的CPU是2.66Ghz(2.66*10^9個時鍾周期每秒)。現在CPU每個時鍾周期可以執行兩條以上的代碼,那么我們就取平均值兩條,於是讓(2660000000*2)/5=1064000000(循環/秒),如果讓CPU工作1秒鍾,然后休息1秒鍾,波形很可能就是鋸齒狀的——先達到一個峰值(>50%),然后跌到一個很低的占用率。
於是嘗試降兩個數量級。用10ms是因為它不大不小,比較接近Windows的調度時間片。如果太小會造成線程頻繁被喚醒和掛起,無形中增加了內核時間的不確定性影響。最后我們得到如下代碼:
package chapter1youxizhile; /** * 【解法一】簡單的方法 * 運行的CPU是2.66Ghz(2.66*10^9個時鍾周期每秒) * @author DELL * */ public class ControlCPU1 { public static void main(String[] args) { for( ; ; ){ for(int i=0;i<10640000;i++); //運行10ms try { Thread.sleep(10); //休眠10ms } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } }
在不斷調整10640000后,我們就可以在一台指定的機器上獲得一條大致穩定的50%CPU占用率直線。
【解法二】使用System.currentTimeMillis()和Thread.sleep()
package chapter1youxizhile; /** * 【解法二】使用System.currentTimeMillis()和Thread.sleep() * 運行的CPU是2.66Ghz(2.66*10^9個時鍾周期每秒) * @author DELL * */ public class CopyOfControlCPU2 { public static void main(String[] args) { int busyTime = 10; //繁忙時間10ms int idleTime = busyTime; //空閑時間 while(true){ long startTime = System.currentTimeMillis(); //獲取系統運行時間 while(System.currentTimeMillis()-startTime<= busyTime) ; try { Thread.sleep(idleTime); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } }
效果如下:
這兩種解法都是假設目前系統上只有當前程序在運行,但實際上,操作系統中有很多程序會同時執行各種各樣的任務,如果此刻其他進程使用了10%的CPU,那么我們的程序應該只能使用40%的CPU,這樣才能達到50%的效果。
怎么做呢?這就需要用到另個工具的幫忙——Perform.exe。如下圖所示:
我們可以寫程序來查詢Perform的值,Mircrosoft .Net Framework提供了PerformanceCounter這一對象,可以方便地得到當前各種性能的數據,包括CPU的使用率。例如下面這個程序:
【解法三】能動態適應的解法
//C# code 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使用率的參數。這個程序可以解答前面提到的問題2。
【解法四】正弦曲線
package chapter1youxizhile; /** * 【解法四】正弦曲線 * @author DELL * */ public class CopyOfCopyOfControlCPU4 { static final double SPLIT = 0.01; //2PI周期步長 static final int COUNT = 200; //取點個數 static final double PI = 3.1415926; static final int INTERVAL = 300; //空閑和繁忙的總時間 public static void main(String[] args) { long busySpan[]; //CPU繁忙時間的數組 long idleSpan[]; //CPU空閑時間的數組 busySpan = new long[COUNT]; idleSpan = new long[COUNT]; int half = INTERVAL/2; double radian = 0.0; for(int i=0;i<COUNT;i++){ busySpan[i] = (long) (half+(Math.sin(PI*radian)*half)); idleSpan[i] = INTERVAL - busySpan[i]; radian += SPLIT; } long startTime = 0; int j = 0; while(true){ j = j%COUNT; //實現周期循環 startTime = System.currentTimeMillis(); while((System.currentTimeMillis()-startTime)<=busySpan[j]) ; try { Thread.sleep(idleSpan[j]); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } j++; } } }
程序運行后任務管理器效果如下: