Antenka帶領你在這裏一個很好的方向。您不應該在每次更新/價值請求時處理和重新創建性能計數器。實例化性能計數器需要花費,並且第一次讀取可能不準確,如下面的引用中所示。你的lock() { ... }
陳述非常廣泛(他們涵蓋了很多陳述),並且會很慢。最好讓你的鎖儘可能小。我正在給Antenka一張質量參考和好建議的投票!
不過,我想我可以爲你提供一個更好的答案。我在監控服務器性能方面有相當多的經驗,並能準確理解您的需求。你的代碼沒有考慮到的一個問題是,無論顯示性能計數器的任何代碼(.aspx,.asmx,控制檯應用程序,winform應用程序等)都可能以任何速率請求此統計信息;它可以每10秒請求一次,也許每秒5次,你不知道也不應該在意。因此,您需要將PerformanceCounter集合代碼與實際報告當前Requests/Second值的代碼進行監視。出於性能方面的原因,我還將向您展示如何在第一次請求時設置性能計數器,然後繼續執行,直到沒有人提出任何請求5秒鐘,然後正確關閉/處理PerformanceCounter。
public class RequestsPerSecondCollector
{
#region General Declaration
//Static Stuff for the polling timer
private static System.Threading.Timer pollingTimer;
private static int stateCounter = 0;
private static int lockTimerCounter = 0;
//Instance Stuff for our performance counter
private static System.Diagnostics.PerformanceCounter pcReqsPerSec;
private readonly static object threadLock = new object();
private static decimal CurrentRequestsPerSecondValue;
private static int LastRequestTicks;
#endregion
#region Singleton Implementation
/// <summary>
/// Static members are 'eagerly initialized', that is,
/// immediately when class is loaded for the first time.
/// .NET guarantees thread safety for static initialization.
/// </summary>
private static readonly RequestsPerSecondCollector _instance = new RequestsPerSecondCollector();
#endregion
#region Constructor/Finalizer
/// <summary>
/// Private constructor for static singleton instance construction, you won't be able to instantiate this class outside of itself.
/// </summary>
private RequestsPerSecondCollector()
{
LastRequestTicks = System.Environment.TickCount;
// Start things up by making the first request.
GetRequestsPerSecond();
}
#endregion
#region Getter for current requests per second measure
public static decimal GetRequestsPerSecond()
{
if (pollingTimer == null)
{
Console.WriteLine("Starting Poll Timer");
// Let's check the performance counter every 1 second, and don't do the first time until after 1 second.
pollingTimer = new System.Threading.Timer(OnTimerCallback, null, 1000, 1000);
// The first read from a performance counter is notoriously inaccurate, so
OnTimerCallback(null);
}
LastRequestTicks = System.Environment.TickCount;
lock (threadLock)
{
return CurrentRequestsPerSecondValue;
}
}
#endregion
#region Polling Timer
static void OnTimerCallback(object state)
{
if (System.Threading.Interlocked.CompareExchange(ref lockTimerCounter, 1, 0) == 0)
{
if (pcReqsPerSec == null)
pcReqsPerSec = new System.Diagnostics.PerformanceCounter("W3SVC_W3WP", "Requests/Sec", "_Total", true);
if (pcReqsPerSec != null)
{
try
{
lock (threadLock)
{
CurrentRequestsPerSecondValue = Convert.ToDecimal(pcReqsPerSec.NextValue().ToString("N2"));
}
}
catch (Exception) {
// We had problem, just get rid of the performance counter and we'll rebuild it next revision
if (pcReqsPerSec != null)
{
pcReqsPerSec.Close();
pcReqsPerSec.Dispose();
pcReqsPerSec = null;
}
}
}
stateCounter++;
//Check every 5 seconds or so if anybody is still monitoring the server PerformanceCounter, if not shut down our PerformanceCounter
if (stateCounter % 5 == 0)
{
if (System.Environment.TickCount - LastRequestTicks > 5000)
{
Console.WriteLine("Stopping Poll Timer");
pollingTimer.Dispose();
pollingTimer = null;
if (pcReqsPerSec != null)
{
pcReqsPerSec.Close();
pcReqsPerSec.Dispose();
pcReqsPerSec = null;
}
}
}
System.Threading.Interlocked.Add(ref lockTimerCounter, -1);
}
}
#endregion
}
好吧,現在來解釋一下。
- 首先你會注意到這個類被設計成一個靜態單例。 你不能加載它的多個副本,它有一個私人構造函數 和和急切地初始化它自己的內部實例。這使得 確保您不會無意中創建相同的多個副本
PerformanceCounter
。
- 接下來,您將在私有構造通知(這將只運行一次 時,第一次訪問類),我們同時創建
PerformanceCounter
,並且將用於輪詢 PerformanceCounter
一個計時器。
- 如果需要 並且獲取其下一個值可用,則計時器的回撥方法將創建
PerformanceCounter
。同樣每5次迭代 我們將看到自從您最後一次請求 PerformanceCounter
的值以來多長時間。如果超過5秒,我們將 關閉輪詢定時器,因爲此時它不需要。如果我們再次需要它,我們可以在以後再次啓動它。
- 現在我們有一個靜態方法叫
GetRequestsPerSecond()
爲您 通話將返回RequestsPerSecond PerformanceCounter
的當前值。
這個實現的好處是你只創建一次性能計數器,然後繼續使用,直到你完成它。它易於使用,因爲您可以隨時隨地撥打RequestsPerSecondCollector.GetRequestsPerSecond()
(.aspx,.asmx,控制檯應用程序,winforms應用程序等)。總是隻有一個PerformanceCounter
,並且無論您多麼快速地撥打RequestsPerSecondCollector.GetRequestsPerSecond()
,它總是會以每秒精確1次的速度進行輪詢。如果您在5秒鐘之內未請求數值,它也會自動關閉並處理PerformanceCounter
。當然,您可以調整定時器間隔和超時毫秒以滿足您的需求。您可以在60秒而不是5秒內輪詢更快和超時。我選擇了5秒鐘,因爲它證明了在visual studio中進行調試時它可以非常快速地工作。一旦你測試它並知道它有效,你可能需要更長的超時時間。
希望這可以幫助您不僅更好地使用PerformanceCounters,而且還可以安全地重用此類,它與您希望顯示統計信息的任何內容相分離。可重複使用的代碼始終是一個優點!
編輯:作爲一個後續問題,如果您想在性能計數器運行時每60秒執行一次清理或保姆任務該怎麼辦?那麼我們已經有計時器每1秒運行一次,並且有一個變量跟蹤我們的循環迭代,稱爲stateCounter
,它在每個計時器回調時遞增。所以,你可以加入一些像這樣的代碼:
// Every 60 seconds I want to close/dispose my PerformanceCounter
if (stateCounter % 60 == 0)
{
if (pcReqsPerSec != null)
{
pcReqsPerSec.Close();
pcReqsPerSec.Dispose();
pcReqsPerSec = null;
}
}
我要指出的是,在這個例子性能計數器不應該「去陳舊」。我相信'請求/秒'應該是一個平均而不是一個移動平均統計。但是這個示例只是說明了您在某個固定時間間隔內對PerformanceCounter
進行任何類型的清理或「保姆」的方式。在這種情況下,我們正在關閉並釋放性能計數器,這將導致它在下一次計時器回調時被重新創建。您可以根據您的使用情況並根據您使用的特定PerformanceCounter對其進行修改。閱讀這個問題/答案的大多數人不需要這樣做。檢查您所需的PerformanceCounter的文檔,看它是否是連續計數,平均值,移動平均值等,並適當調整您的實施。
和公正的記錄,我主持這個類中IIS網站(那當然是在應用程序池具有管理權限的託管)和ASMX服務調用方法... – kape123