By Joe Mayo
In This Chapter
- Accessing Built-in Performance Counters
- Implementing Timers
- Building a Customized Performance Counter
- Analyzing Performance with Sampling
It's agreed that a program must run correctly and produce accurate
results, but in many systems this isn't enough. Enterprise-class
applications are of such mass that they must also be scalable. Verifying the
scalability of an application traditionally requires specialized tools and
bolted-on functionality to support monitoring. Now there's help, using the
performance counter capability of the System.Diagnostics namespace.
Performance counters present an object framework for supporting application
monitoring. The framework hooks into the operating system performance counter
system to access available counters. Additionally, the performance counter
framework can be extended for customized counters and data sampling. Such
samples may be collected efficiently with timers, which, as their name suggests,
enable periodic execution of logic via specified time intervals. By using either
built-in or customized performance counters, a program can be monitored under
various conditions to verify its performance and scalability.
Accessing Built-in Performance Counters
The performance counter framework provides access to existing operating
system counters. The help files associated with the operating system performance
monitor application have more information on what counters are available.
Using a performance counter involves declaring a PerformanceCounter
object, initializing its properties as desired and requesting the counter value
at various intervals to watch performance. The program in Listings 32.1 and 32.2
comprise a fictitious ordering system that demonstrates the use of system
performance counters. The program is sufficiently equipped to degrade system
performance, where the effects can be observed by watching the performance
counter.
Listing 32.1 System Performance Counter Demo:
OrderClient.cs
using System;
using System.Drawing;
using System.Collections;
using System.ComponentModel;
using System.Windows.Forms;
using System.Data;
using System.Threading;
using System.Diagnostics;
namespace OrderingClient
{
/// <summary>
/// Performance Counter Demo.
/// </summary>
public class OrderClient : System.Windows.Forms.Form
{
private System.Windows.Forms.Label maxOrdLbl;
private System.Windows.Forms.Label curOrdLbl;
private System.Windows.Forms.TextBox maxOrdTxt;
private System.Windows.Forms.Label curOrdResultLbl;
private System.Windows.Forms.Button updateBtn;
private int maxOrders;
private int curOrders;
private OrderProcessor orderProc;
private System.Windows.Forms.Timer orderTimer;
private System.Windows.Forms.Timer countTimer;
private System.Windows.Forms.Label threadLbl;
private System.Windows.Forms.Label threadResultLbl;
private System.Diagnostics.PerformanceCounter
threadCounter;
private System.ComponentModel.IContainer components;
public OrderClient()
{
InitializeComponent();
maxOrders = 10;
maxOrdTxt.Text = maxOrders.ToString();
orderProc = new OrderProcessor();
curOrders = orderProc.CurNoOrders;
curOrdResultLbl.Text = curOrders.ToString();
}
/// <summary>
/// Clean up any resources being used.
/// </summary>
protected override void Dispose( bool disposing )
{
if( disposing )
{
if (components != null)
{
components.Dispose();
}
}
base.Dispose( disposing );
}
private void InitializeComponent()
{
this.components =
new System.ComponentModel.Container();
this.maxOrdTxt =
new System.Windows.Forms.TextBox();
this.updateBtn =
new System.Windows.Forms.Button();
this.maxOrdLbl =
new System.Windows.Forms.Label();
this.curOrdLbl =
new System.Windows.Forms.Label();
this.orderTimer =
new System.Windows.Forms.Timer(
this.components);
this.curOrdResultLbl =
new System.Windows.Forms.Label();
this.countTimer =
new System.Windows.Forms.Timer(
this.components);
this.threadLbl =
new System.Windows.Forms.Label();
this.threadResultLbl =
new System.Windows.Forms.Label();
this.threadCounter =
new System.Diagnostics.PerformanceCounter();
((System.ComponentModel.ISupportInitialize)
(this.threadCounter)).BeginInit();
this.SuspendLayout();
this.maxOrdTxt.Location =
new System.Drawing.Point(152, 24);
this.maxOrdTxt.Name = "maxOrdTxt";
this.maxOrdTxt.TabIndex = 2;
this.maxOrdTxt.Text = "";
this.maxOrdTxt.TextAlign =
System.Windows.Forms.HorizontalAlignment.Right;
this.updateBtn.Location =
new System.Drawing.Point(104, 152);
this.updateBtn.Name = "updateBtn";
this.updateBtn.TabIndex = 4;
this.updateBtn.Text = "Update";
this.updateBtn.Click +=
new System.EventHandler(this.updateBtn_Click);
this.maxOrdLbl.Location =
new System.Drawing.Point(40, 24);
this.maxOrdLbl.Name = "maxOrdLbl";
this.maxOrdLbl.TabIndex = 0;
this.maxOrdLbl.Text = "Max Orders:";
this.maxOrdLbl.TextAlign =
System.Drawing.ContentAlignment.MiddleRight;
this.curOrdLbl.Location =
new System.Drawing.Point(40, 64);
this.curOrdLbl.Name = "curOrdLbl";
this.curOrdLbl.TabIndex = 1;
this.curOrdLbl.Text = "Current Orders:";
this.curOrdLbl.TextAlign =
System.Drawing.ContentAlignment.MiddleRight;
this.orderTimer.Enabled = true;
this.orderTimer.Interval = 2000;
this.orderTimer.Tick +=
new System.EventHandler(this.orderTimer_Tick);
this.curOrdResultLbl.BorderStyle =
System.Windows.Forms.BorderStyle.Fixed3D;
this.curOrdResultLbl.Location =
new System.Drawing.Point(152, 64);
this.curOrdResultLbl.Name = "curOrdResultLbl";
this.curOrdResultLbl.Size =
new System.Drawing.Size(100, 20);
this.curOrdResultLbl.TabIndex = 3;
this.curOrdResultLbl.TextAlign =
System.Drawing.ContentAlignment.MiddleRight;
this.countTimer.Enabled = true;
this.countTimer.Interval = 1000;
this.countTimer.Tick +=
new System.EventHandler(this.countTimer_Tick);
this.threadLbl.Location =
new System.Drawing.Point(40, 104);
this.threadLbl.Name = "threadLbl";
this.threadLbl.TabIndex = 1;
this.threadLbl.Text = "Thread Count:";
this.threadLbl.TextAlign =
System.Drawing.ContentAlignment.MiddleRight;
this.threadResultLbl.BorderStyle =
System.Windows.Forms.BorderStyle.Fixed3D;
this.threadResultLbl.Location =
new System.Drawing.Point(152, 104);
this.threadResultLbl.Name = "threadResultLbl";
this.threadResultLbl.Size =
new System.Drawing.Size(100, 20);
this.threadResultLbl.TabIndex = 3;
this.threadResultLbl.TextAlign =
System.Drawing.ContentAlignment.MiddleRight;
this.threadCounter.CategoryName =
".NET CLR LocksAndThreads";
this.threadCounter.CounterName =
"# of current physical Threads";
this.threadCounter.InstanceName =
"OrderingClient";
this.AutoScaleBaseSize =
new System.Drawing.Size(5, 13);
this.ClientSize =
new System.Drawing.Size(288, 197);
this.Controls.AddRange(
new System.Windows.Forms.Control[] {
this.threadLbl,
this.threadResultLbl,
this.updateBtn,
this.curOrdResultLbl,
this.maxOrdTxt,
this.curOrdLbl,
this.maxOrdLbl});
this.Name = "OrderClient";
this.Text = "Order Client";
((System.ComponentModel.ISupportInitialize)
(this.threadCounter)).EndInit();
this.ResumeLayout(false);
}
static void Main()
{
Application.Run(new OrderClient());
}
private void updateBtn_Click(object sender, System.EventArgs e)
{
maxOrders = Convert.ToInt32(maxOrdTxt.Text);
}
private void orderTimer_Tick(object sender, System.EventArgs e)
{
orderTimer.Enabled = false;
Thread th = new Thread(new ThreadStart(ProcessOrders));
th.Start();
orderTimer.Enabled = true;
}
private void countTimer_Tick(object sender, System.EventArgs e)
{
countTimer.Enabled = false;
curOrdResultLbl.Text = orderProc.CurNoOrders.ToString();
threadResultLbl.Text = threadCounter.NextValue().ToString();
countTimer.Enabled = true;
}
private void ProcessOrders()
{
for (curOrders = orderProc.CurNoOrders;
curOrders <= maxOrders;
curOrders++)
{
curOrdResultLbl.Text = curOrders.ToString();
orderProc.ProcessOrder();
}
}
}
}
Listing 32.2 Server Component of System Performance Counter Demo:
OrderProcessor.cs
using System;
using System.Threading;
namespace OrderingClient
{
/// <summary>
/// Summary description for OrderProcessor.
/// </summary>
public class OrderProcessor
{
private static int curNoOrders = 0;
private Random rand;
public OrderProcessor()
{
rand = new Random();
}
public int ProcessOrder()
{
Thread th = new Thread(new ThreadStart(doOrder));
th.Start();
curNoOrders++;
return 0;
}
public int CurNoOrders
{
get
{
return curNoOrders;
}
set
{
curNoOrders = value;
}
}
private void doOrder()
{
for (int delay = rand.Next(10000000);
delay >= 0;
delay)
;
curNoOrders;
}
}
}
The performance counter framework belongs to the System.Diagnostics
namespace. Performance counters are declared like any other class as
follows:
private System.Diagnostics.PerformanceCounter
threadCounter;
This particular performance counter keeps track of the number of .NET Common
Language Runtime (CLR) threads. There are three pertinent properties of a
performance counter that are required: CategoryName,
CounterName, and InstanceName as shown next.
this.threadCounter.CategoryName =
".NET CLR LocksAndThreads";
this.threadCounter.CounterName =
"# of current physical Threads";
this.threadCounter.InstanceName =
"OrderingClient";
Performance counters are broken into categories that help organize each
counter into a logical related group. The preceding example sets the category
for the threadCounter object to ".NET CLR
LocksAndThreads". An examination of this category in the .NET
Framework Documentation shows that this category has counters for different
types of threads and other counters associated with thread synchronization. This
example assigns the "# of current physical Threads" counter
to the CounterName property of the threadCounter object. The
InstanceName property holds the name of the executable file whose count
property will be monitored.
To get the value of the counter, call the NextValue() method of the
PerformanceCounter object. The following example shows how to do
this:
threadResultLbl.Text = threadCounter.NextValue().ToString();
The example converts the integer value returned from the NextValue()
method into a string and places it into a Windows forms label control for presentation
onscreen.

Fig 32.1
The above picture shows what the code
from Listings 32.1 and 32.2 look like when compiled and executed. Increasing
the number in the Max Orders text box stresses the system. This can be observed
by watching the numbers change more sluggishly, indicating performance degradation.
Here are the compilation instructions:
csc /t:winexe /out:OrderingClient.exe OrderClient.cs
OrderProcessor.cs
Figure 32.1
A system performance counter.
The program from Listings 32.1 and 32.2 use threads extensively.
OrderClient uses threads to execute its loop efficiently. It finishes
quickly so it doesn't hold up any other program activities, such as the
ability to update Max Orders, update the count fields, and execute timers.
The OrderProcessor class uses threads so it can accept orders
efficiently without making the client block for each order. Otherwise, there
would be no telling how long it could take to process an order, because the
program is set to take a random amount of time for each order. This simulates
the nature of many ordering systems, which typically have multiple types of
orders and several options or variables that make the amount of time for each
order practically unpredictable.
Tip
As the world turns, Moore's
law (which states that the speed of computers doubles every 1824 months)
has my faithful but inadequate computer dragging behind in performance. If you
don't experience significant performance hits when incrementing Max
Orders in this program, bump up the number of zeros in the
rand.Next() method in the for loop initializer of the
doOrder() method in Listing 32.2.
Implementing Timers
It would be easy to use existing C# constructs, such as sleeping threads or
for and while loops, to control the periodic collection of
performance counter data. The primary problems with these methods are their
synchronous nature. Furthermore, loops like for and while
deliver a significant performance hit. A better solution for performing logic
via specified intervals is the timer.
A timer can be set for a specified time interval, executing a callback
routine whenever that interval elapses. The primary benefit of this approach is
the asynchronous behavior of the timer, which delivers much better performance
than the synchronous methods discussed earlier. Just set the timer interval,
assign a callback routine to execute, and then move on and process the rest of
the program logic. The following example shows how timers are declared:
private System.Windows.Forms.Timer orderTimer;
private System.Windows.Forms.Timer countTimer;
These timers are members of the System.Windows.Forms namespace. The
orderTimer will fire periodically to make sure the number of orders
being processed go up to, but not over, the maximum number of orders. The
countTimer fires periodically to update the number of orders being
processed and to get and display the current value of the threadCounter
performance counter. Here's an example of how the timers are set up:
this.orderTimer.Enabled = true;
this.orderTimer.Interval = 2000;
this.orderTimer.Tick +=
new System.EventHandler(this.orderTimer_Tick);
this.countTimer.Enabled = true;
this.countTimer.Interval = 1000;
this.countTimer.Tick +=
new System.EventHandler(this.countTimer_Tick);
Both of these timers have their Enabled properties set to
true, meaning that the timers are turned on. A timer can be turned off
by setting the Enabled property to false; this is necessary
when a program is in the middle of a callback and doesn't want the timer
firing while a previous callback based on that timer is still executing.
Setting the Interval property to 1000 makes the timer tick
approximately every second. Thus, the orderTimer will tick in about two
seconds, and the count timer will tick about once per second.
Warning
Being based on the underlying
operating system timer, don't bet on timers having a great degree of
accuracy. This is because there are various operating system events that may
preclude the tick event from firing on time; therefore, the safest assumption to
make with timers is that they provide an approximate timing mechanism.
Callback routines are attached to the Tick event of a timer with the
EventHandler delegate. The orderTimer timer calls the
ordertimer_Tick() method, and the countTimer timer calls the
countTimer_Tick() method when their respective Tick events
fire. Listing 32.1 has the full code that shows what these routines do when
their Tick event fires.
Building a Customized Performance Counter
Often the system performance counters are enough for monitoring a
system's performance. However, sometimes you need a specialized counter
that gives a unique picture of what's happening in a specific program.
Making customized performance counters is possible, due to the extensible nature
of the performance counter framework.
Implementing a customized performance counter requires creating a new counter
type and a new category to hold the new counter. The performance counter will be
instantiated with the new counter and category definitions. Additional logic is
also necessary to load the custom performance counter with program specific
data. Listings 32.3 and 32.4 show how to implement custom performance
counters.
Listing 32.3 Client Using Data from Custom Performance Counter:
CustomOrderClient.cs
using System;
using System.Drawing;
using System.Collections;
using System.ComponentModel;
using System.Windows.Forms;
using System.Data;
using System.Threading;
using System.Diagnostics;
namespace OrderingClient
{
/// <summary>
/// Summary description for Form1.
/// </summary>
public class CustomClient : System.Windows.Forms.Form
{
private System.Windows.Forms.Label maxOrdLbl;
private System.Windows.Forms.Label curOrdLbl;
private System.Windows.Forms.TextBox maxOrdTxt;
private System.Windows.Forms.Label curOrdResultLbl;
private System.Windows.Forms.Button updateBtn;
private int maxOrders;
private int curOrders;
private CustomOrderProcessor orderProc;
private System.Windows.Forms.Timer orderTimer;
private System.Windows.Forms.Timer countTimer;
private System.Windows.Forms.Label threadLbl;
private System.Windows.Forms.Label threadResultLbl;
private System.Diagnostics.PerformanceCounter
threadCounter;
private System.ComponentModel.IContainer components;
public CustomClient()
{
InitializeComponent();
maxOrders = 10;
maxOrdTxt.Text = maxOrders.ToString();
orderProc = new CustomOrderProcessor();
curOrders = orderProc.CurNoOrders;
curOrdResultLbl.Text = curOrders.ToString();
}
/// <summary>
/// Clean up any resources being used.
/// </summary>
protected override void Dispose( bool disposing )
{
if( disposing )
{
if (components != null)
{
components.Dispose();
}
}
base.Dispose( disposing );
}
private void InitializeComponent()
{
this.components =
new System.ComponentModel.Container();
this.maxOrdTxt =
new System.Windows.Forms.TextBox();
this.threadLbl =
new System.Windows.Forms.Label();
this.orderTimer =
new System.Windows.Forms.Timer(this.components);
this.updateBtn =
new System.Windows.Forms.Button();
this.threadResultLbl =
new System.Windows.Forms.Label();
this.curOrdResultLbl =
new System.Windows.Forms.Label();
this.threadCounter =
new System.Diagnostics.PerformanceCounter();
this.curOrdLbl =
new System.Windows.Forms.Label();
this.countTimer =
new System.Windows.Forms.Timer(this.components);
this.maxOrdLbl =
new System.Windows.Forms.Label();
((System.ComponentModel.ISupportInitialize)
(this.threadCounter)).BeginInit();
this.SuspendLayout();
this.maxOrdTxt.Location =
new System.Drawing.Point(152, 24);
this.maxOrdTxt.Name = "maxOrdTxt";
this.maxOrdTxt.TabIndex = 2;
this.maxOrdTxt.Text = "";
this.maxOrdTxt.TextAlign =
System.Windows.Forms.HorizontalAlignment.Right;
this.threadLbl.Location =
new System.Drawing.Point(40, 104);
this.threadLbl.Name = "threadLbl";
this.threadLbl.TabIndex = 1;
this.threadLbl.Text = "Thread Count:";
this.threadLbl.TextAlign =
System.Drawing.ContentAlignment.MiddleRight;
this.orderTimer.Enabled = true;
this.orderTimer.Interval = 2000;
this.orderTimer.Tick +=
new System.EventHandler(this.orderTimer_Tick);
this.updateBtn.Location =
new System.Drawing.Point(104, 152);
this.updateBtn.Name = "updateBtn";
this.updateBtn.TabIndex = 4;
this.updateBtn.Text = "Update";
this.updateBtn.Click +=
new System.EventHandler(this.updateBtn_Click);
this.threadResultLbl.BorderStyle =
System.Windows.Forms.BorderStyle.Fixed3D;
this.threadResultLbl.Location =
new System.Drawing.Point(152, 104);
this.threadResultLbl.Name = "threadResultLbl";
this.threadResultLbl.Size =
new System.Drawing.Size(100, 20);
this.threadResultLbl.TabIndex = 3;
this.threadResultLbl.TextAlign =
System.Drawing.ContentAlignment.MiddleRight;
this.curOrdResultLbl.BorderStyle =
System.Windows.Forms.BorderStyle.Fixed3D;
this.curOrdResultLbl.Location =
new System.Drawing.Point(152, 64);
this.curOrdResultLbl.Name = "curOrdResultLbl";
this.curOrdResultLbl.Size =
new System.Drawing.Size(100, 20);
this.curOrdResultLbl.TabIndex = 3;
this.curOrdResultLbl.TextAlign =
System.Drawing.ContentAlignment.MiddleRight;
this.threadCounter.CategoryName =
".NET CLR LocksAndThreads";
this.threadCounter.CounterName =
"# of current physical Threads";
this.threadCounter.InstanceName =
"CustomClient";
this.curOrdLbl.Location =
new System.Drawing.Point(40, 64);
this.curOrdLbl.Name = "curOrdLbl";
this.curOrdLbl.TabIndex = 1;
this.curOrdLbl.Text = "Current Orders:";
this.curOrdLbl.TextAlign =
System.Drawing.ContentAlignment.MiddleRight;
this.countTimer.Enabled = true;
this.countTimer.Interval = 1000;
this.countTimer.Tick +=
new System.EventHandler(this.countTimer_Tick);
this.maxOrdLbl.Location =
new System.Drawing.Point(40, 24);
this.maxOrdLbl.Name = "maxOrdLbl";
this.maxOrdLbl.TabIndex = 0;
this.maxOrdLbl.Text = "Max Orders:";
this.maxOrdLbl.TextAlign =
System.Drawing.ContentAlignment.MiddleRight;
this.AutoScaleBaseSize =
new System.Drawing.Size(5, 13);
this.ClientSize =
new System.Drawing.Size(288, 197);
this.Controls.AddRange(
new System.Windows.Forms.Control[] {
this.threadLbl,
this.threadResultLbl,
this.updateBtn,
this.curOrdResultLbl,
this.maxOrdTxt,
this.curOrdLbl,
this.maxOrdLbl});
this.Name = "CustomClient";
this.Text = "Custom Client";
this.Closing +=
new System.ComponentModel.CancelEventHandler(
this.CustomClient_Closing);
((System.ComponentModel.ISupportInitialize)
(this.threadCounter)).EndInit();
this.ResumeLayout(false);
}
static void Main()
{
Application.Run(new CustomClient());
}
private void updateBtn_Click(
object sender, System.EventArgs e)
{
maxOrders = Convert.ToInt32(maxOrdTxt.Text);
}
private void orderTimer_Tick(
object sender, System.EventArgs e)
{
orderTimer.Enabled = false;
Thread th = new Thread(
new ThreadStart(ProcessOrders));
th.Start();
orderTimer.Enabled = true;
}
private void countTimer_Tick(
object sender, System.EventArgs e)
{
countTimer.Enabled = false;
curOrdResultLbl.Text =
orderProc.CurNoOrders.ToString();
threadResultLbl.Text =
threadCounter.NextValue().ToString();
countTimer.Enabled = true;
}
private void ProcessOrders()
{
for (curOrders = orderProc.CurNoOrders;
curOrders <= maxOrders;
curOrders++)
{
curOrdResultLbl.Text = curOrders.ToString();
orderProc.ProcessOrder();
}
}
private void CustomClient_Closing(object sender,
System.ComponentModel.CancelEventArgs e)
{
orderProc.Dispose();
}
}
}
Listing 32.4 Server Implementing a Custom Performance Counter:
CustomOrderProcessor.cs
using System;
using System.Threading;
using System.Diagnostics;
namespace OrderingClient
{
/// <summary>
/// Summary description for CustomOrderProcessor.
/// </summary>
public class CustomOrderProcessor : IDisposable
{
private PerformanceCounter orderCounter;
private Random rand;
public CustomOrderProcessor()
{
rand = new Random();
CounterCreationDataCollection myCounters =
new CounterCreationDataCollection();
CounterCreationData myCounterCreationData =
new CounterCreationData();
myCounterCreationData.CounterName =
"Order Count";
myCounterCreationData.CounterHelp =
"Displays number of orders being processed.";
myCounterCreationData.CounterType =
PerformanceCounterType.NumberOfItems32;
myCounters.Add(myCounterCreationData);
if (PerformanceCounterCategory.Exists(
"Order Processor"))
{
PerformanceCounterCategory.Delete(
"Order Processor");
}
PerformanceCounterCategory.Create(
"Order Processor",
"OrderProcessor class counters",
myCounters);
orderCounter = new PerformanceCounter(
"Order Processor",
"Order Count",
false);
orderCounter.RawValue = 0;
}
public int ProcessOrder()
{
Thread th = new Thread(new ThreadStart(doOrder));
th.Start();
CurNoOrders++;
return 0;
}
public int CurNoOrders
{
get
{
return (int)orderCounter.NextValue();
}
set
{
orderCounter.RawValue = value;
}
}
private void doOrder()
{
for (int delay = rand.Next(1000000);
delay >= 0;
delay)
;
CurNoOrders;
}
public void Dispose()
{
PerformanceCounterCategory.Delete(
"Order Processor");
}
}
}
The interesting bits of this program are in Listing 32.4. The custom counter
is initialized in the constructor, and the updates are managed with the
CurNoOrders property. The two primary classes supporting custom
counters are the CounterCreationDataCollection and
CounterCreationData, which are each instantiated with default
constructors, as shown here:
CounterCreationDataCollection myCounters =
new CounterCreationDataCollection();
CounterCreationData myCounterCreationData =
new CounterCreationData();
The CounterCreationData class holds counter definition properties
that must be set to create a new counter. The CounterName property is a
user-defined name of a counter. The CounterType property may be any
member of the PerformanceCounterType enum, which are listed in Table
32.1. The following code sets the CounterCreationData properties,
including the CounterHelp property, which is a description of the
custom counter:
myCounterCreationData.CounterName =
"Order Count";
myCounterCreationData.CounterHelp =
"Displays number of orders being processed.";
myCounterCreationData.CounterType =
PerformanceCounterType.NumberOfItems32;
Table 32.1 Members of the PerformanceCounterType Enum
|
Counter Name
|
Description
|
|
AverageBase
|
Denominator for AverageCount32 and AverageCount64
|
|
AverageCount64
|
64-bit average count
|
|
AverageCount32
|
32-bit average count
|
|
AverageTimer32
|
32-bit average elapsed time
|
|
CounterDelta32
|
32-bit difference between counts
|
|
CounterDelta64
|
64-bit difference between counts
|
|
CounterMultiBase
|
Denominator for CounterMultiTimer, CounterMultiTimerInverse,
CounterMultiTimer100Ns, and CounterMultiTimer100NsInverse
|
|
CounterMultiTimer
|
Multiple time samplingsin use
|
|
CounterMultiTimer100Ns
|
Multiple time samplings in 100 nanosecond units
|
|
CounterMultiTimerInverse
|
Multiple time samplingsnot in use
|
|
CounterTimer
|
Time samplingin use
|
|
CounterTimerInverse
|
Time samplingnot in use
|
|
CountPerTimeInterval32
|
32-bit count per time interval
|
|
CountPerTimeInterval64
|
64-bit count per time interval
|
|
ElapsedTime
|
Difference between timer start and sample
|
|
NumberOfItems32
|
32-bit count
|
|
NumberOfItems64
|
64-bit count
|
|
NumberOfItemsHEX32
|
32-bit hexadecimal count
|
|
NumberOfItemsHEX64
|
64-bit hexadecimal count
|
|
RateOfCountsPerSecond32
|
32-bit number of counts per second
|
|
RateOfCountsPerSecond64
|
64-bit number of counts per second
|
|
RawBase
|
Denominator for RawFraction
|
|
RawFraction
|
Numerator of a fractional count
|
|
SampleBase
|
Denominator representing number of samplings
|
|
SampleCounter
|
Number of ones returned from 0 or 1 count
|
|
SampleFraction
|
Percentage of ones returned from 0 or 1 count
|
|
Timer100Ns
|
Time in 100 nanosecond unitsin use
|
|
Timer100NsInverse
|
Time in 100 nanosecond unitsnot in use
|
Once the new counter is defined, add the
CounterCreationData object to the
CounterCreationDataCollection object. This completes the definition of
the counter, and now the counter must be added to a category. To create the
CounterCategory, call the static Create() method of the
PerformanceCounterCategory class with three parameters: category name,
category description, and the CounterCreationDataCollection object just
described. As you may suspect, multiple counters may be added to a category by
just adding more CounterCreationData counters to the
CounterCreationDataCollection object used as the third parameter to the
Create() method of the PerformanceCounterCategory class.
Here's the definition of the customized counter with a custom category:
myCounters.Add(myCounterCreationData);
if (PerformanceCounterCategory.Exists(
"Order Processor"))
{
PerformanceCounterCategory.Delete(
"Order Processor");
}
PerformanceCounterCategory.Create(
"Order Processor",
"OrderProcessor class counters",
myCounters);
This example also contains a check for whether the new category exists. If
this is true, the category is deleted before it is recreated. If a performance
counter category already exists, then it can't be recreated, throwing a
runtime exception. This program could just as well have used an exception
handler around this code, which may be better form. However, to be instructive,
this example shows how to use the Exists() and Delete()
methods of the PerformanceCounterCategory class. There's also a
Delete() method call in the Dispose() method so the program
doesn't leave counters laying around unnecessarily.
The performance counter object for this new custom performance counter is
declared the same as any other performance counter. One important item to
address is that a program must manage the custom counter itself, updating its
value as appropriate. This performance counter value is initialized by setting
its RawValue property to 0, as the following code shows:
orderCounter = new PerformanceCounter(
"Order Processor",
"Order Count",
false);
orderCounter.RawValue = 0;
Subsequent management of the custom performance counter resides in the
CurNoOrders property. The get accessor obtains the
NextValue(), a float result, and casts it to an int
before returning the value. The set accessor directly sets the
counter's RawValue property. Here's the CurNoOrders
property:
public int CurNoOrders
{
get
{
return (int)orderCounter.NextValue();
}
set
{
orderCounter.RawValue = value;
}
}
Custom performance counters present a unique view of special conditions within
a program. They provide insight not available with the generalized view of system
performance counters.

Figure 32.2
Fig 32.2 shows the
executed program from Listings 32.3 and 32.4. Here are the compilation instructions:
csc /t:winexe /out:CustomClient.exe CustomClient.cs
CustomOrderProcessor.cs
Figure 32.2
A custom performance counter.