2012-08-09 33 views
12

我正在使用此類作爲啓動進程並給它一些輸入並等待它變爲空閒的測試類別的基類然後再給它更多的投入。即使進程沒有退出,進程的性能計數器實例名稱是否可以更改

public abstract class TestProcessLaunchingBase 
{ 
    protected PerformanceCounter PerfCounter { get; set; } 

    protected void WaitForProcessIdle() 
    { 
     while (true) 
     { 
      float oldValue = PerfCounter.NextValue(); 

      Thread.Sleep(1000); 

      float nextValue = PerfCounter.NextValue(); 

      if (nextValue == 0) 
       break; 
     } 
    } 

    protected void FindSpawnedProcessPerfCounter(int processId) 
    { 
     PerformanceCounterCategory cat = new PerformanceCounterCategory("Process"); 
     string[] instances = cat.GetInstanceNames(); 
     foreach (string instance in instances) 
     { 
      using (PerformanceCounter cnt = new PerformanceCounter("Process", "ID Process", instance, true)) 
      { 
       int val = (int)cnt.RawValue; 
       if (val == processId) 
       { 
        PerfCounter = new PerformanceCounter("Process", "% Processor Time", instance); 
        break; 
       } 
      } 

     } 

     Assert.IsNotNull(PerfCounter, "Failed to perf counter"); 
    } 
} 

這些測試偶爾會失敗,因爲PerfCounter.NextValue()拋出

System.InvalidOperationException 實例 'foobar的#2' 中指定的類別

好像實例名稱不存在的性能計數器不是持久的。

如果有三個foobar的過程中,他們可能有實例名稱

  • foobar的 PID 5331
  • foobar的#1 PID 5332
  • foobar的#2 PID 5333

好像是pid 5332 exits foobar#2變成foobar#1

問題:

  1. 這是一個記錄的行爲?你不能堅持一個表現計數器嗎?你每次都必須查看它嗎?

  2. 另外,有可以給處理器時間名爲foobar的

回答

9

我已經在過去面對這個問題的所有進程性能計數器。實例名稱的ProcessName#InstanceNumber模式顯然是微軟一個糟糕的選擇,你知道爲什麼:)

所以基本上你有兩種選擇:

1)創建一個新的PerformanceCounter實例每次使用FindSpawnedProcessPerfCounter方法。

2)按照KB281884中描述的步驟將模式從ProcessName#InstanceNumber更改爲ProcessName_ProcessID

第一種解決方案的問題是,每次需要一些CPU時間來構建新實例。

第二個解決方案的問題是註冊表修改還會影響所有也在使用此性能計數器的程序。它需要在啓動你的應用程序之前修改註冊表。

最後一個選項是根本不使用性能計數器。如果您只對ProcessorTime信息感興趣,則可以使用P/Invoke調用一些Kernel32函數來檢索它。

編輯:

Process類還提供UserProcessorTimePrivilegedProcessorTime (kernel processor time)性質。兩者都返回TimeSpan實例(=時間量),因此爲了檢索處理器時間的百分比,您必須自己做一些計算(涉及刷新週期和處理器時間)。

+0

P/Invoke:'GetProcessTimes'給出給定進程的用戶和內核模式時間。我們需要先用'OpenProcess'獲得進程句柄,如果這個應用程序的另一個實例關閉,它肯定不會改變。 – 2012-08-13 17:43:17

0

如果必須使用性能計數器,我的做法是在遇到異常時重新創建我的性能計數器。

當一個異常被拋出,處置舊的性能計數器,然後創建一個使用進程ID爲您foobar的測試的所有實例的新的性能計數器實例。有一個不錯的計算器發佈在這裏:Performance Counter by Process ID instead of name?

我假設你的語句「啓動一個進程並給它一些輸入」,你在啓動它之後持有進程id。所以你總是有一組你想要監控的運行測試過程。

有了這個技術,你只會招致性能損失,當你打一個例外。

正如@ ken2k已經指出的那樣,你可以改變性能計數器一致的命名約定。根據我的經驗,性能計數器有時可能會遇到任何數量的例外情況,看似出乎意料的理由因此,即使您更改了性能計數器名稱,如果有必要,也可以重新創建性能計數器。

+0

我不認爲通過在異常情況下重新創建實例來解決問題不是一個好主意。用OP的例子:如果ID 5333的進程退出,那麼實例「foobar#2」會拋出一個異常,很好。但是,如果ID 5332的進程退出,使用「foobar#1」不會拋出任何異常:它只是「指向」ID 5333的進程。沒有例外,錯誤的價值。 – ken2k 2012-08-20 16:49:35

+0

@ ken2k,我在帖子中暗示複數。即:重新創建性能計數器的集合,而不僅僅是拋出異常的那個。也許我還不夠清楚。最終,他會希望將結果綁定到進程ID或聚合這些值。如果進行彙總,只要它仍然是一個測試過程,他不需要關心foobar#1的變化。如果他想將數據綁定到進程ID,則需要執行上述操作或考慮使用.Net Process類的建議等。 – BenSwayne 2012-08-20 17:17:18