How To: Monitor the ASP.NET Thread Pool Using Custom Counters
Retired Content |
---|
This content is outdated and is no longer being maintained. It is provided as a courtesy for individuals who are still using these technologies. This page may contain URLs that were valid when originally published, but now link to sites or pages that no longer exist. |
Improving .NET Application Performance and Scalability
J.D. Meier, Srinath Vasireddy, Ashish Babbar, and Alex Mackman
Microsoft Corporation
May 2004
Related Links
Home Page for Improving .NET Application Performance and Scalability
Chapter 4, Architecture and Design Review of a .NET Application for Performance and Scalability
Chapter 6, Improving ASP.NET Performance
Checklist: ADO.NET Performance
Send feedback to Scale@microsoft.com
Summary: This How To shows you how to monitor the ASP.NET thread pool by creating a set of custom performance counters. The How To also shows you how to modify the maximum number of threads available to an ASP.NET application.
Contents
Applies To
Overview
Create Custom Performance Counters
Create an ASP.NET Application to Refresh the Counters
View the Counters in Performance Monitor
Run a Test Page that Uses Threads
Additional Resources
Applies To
- Microsoft® .NET Framework version 1.1
Overview
In this How To, you monitor the ASP.NET thread pool by creating a set of custom performance counters. You then instrument an ASP.NET application by using these counters. From the ASP.NET application, you set the performance counter values every half second.
This technique enables you to monitor threading activity in your ASP.NET application and to diagnose thread-related performance issues and bottlenecks.
**Note **This solution is intended for development and testing purposes only. It is designed to help you learn about, and monitor, threading and threading behavior in an ASP.NET application.
Create Custom Performance Counters
In this section, you will create four custom performance counters as defined in Table 1. All counters will be added to the category named ASP.NET Thread Pool, and all counters will be of type PerformanceCounterType.NumberOfItems32.
Table 1: Custom Performance Counters
Counter name | Description |
---|---|
Available Worker Threads | The difference between the maximum number of thread-pool worker threads and the number currently active. |
Available IO Threads | The difference between the maximum number of thread-pool I/O threads and the number currently active. |
Max Worker Threads | The number of requests to the thread pool that can be active concurrently. All requests above that number remain queued until thread-pool worker threads become available. |
Max IO Threads | The number of requests to the thread pool that can be active concurrently. All requests above that number remain queued until thread-pool I/O threads become available. |
Although it is possible to use the Microsoft Visual Studio® .NET Server Explorer to create performance counters manually, the code that follows shows how to create them from a console application.
Create a Console Application
Create an empty source file named CreateASPNETThreadCounters.cs and add the following code. This code creates a simple console application that in turn creates the relevant custom performance counters and applies the relevant settings to the Microsoft Windows® registry.
using System;
using System.Diagnostics;
class MyAspNetThreadCounters
{
[STAThread]
static void Main(string[] args)
{
CreateCounters();
Console.WriteLine("MyAspNetThreadCounters performance counter category " +
"is created. [Press Enter]");
Console.ReadLine();
}
public static void CreateCounters()
{
CounterCreationDataCollection col =
new CounterCreationDataCollection();
// Create custom counter objects
CounterCreationData counter1 = new CounterCreationData();
counter1.CounterName = "Available Worker Threads";
counter1.CounterHelp = "The difference between the maximum number " +
"of thread pool worker threads and the " +
"number currently active.";
counter1.CounterType = PerformanceCounterType.NumberOfItems32;
CounterCreationData counter2 = new CounterCreationData();
counter2.CounterName = "Available IO Threads";
counter2.CounterHelp = "The difference between the maximum number of " +
"thread pool IO threads and the number "+
"currently active.";
counter2.CounterType = PerformanceCounterType.NumberOfItems32;
CounterCreationData counter3 = new CounterCreationData();
counter3.CounterName = "Max Worker Threads";
counter3.CounterHelp = "The number of requests to the thread pool "+
"that can be active concurrently. All "+
"requests above that number remain queued until " +
"thread pool worker threads become available.";
counter3.CounterType = PerformanceCounterType.NumberOfItems32;
CounterCreationData counter4 = new CounterCreationData();
counter4.CounterName = "Max IO Threads";
counter4.CounterHelp = "The number of requests to the thread pool " +
"that can be active concurrently. All "+
"requests above that number remain queued until " +
"thread pool IO threads become available.";
counter4.CounterType = PerformanceCounterType.NumberOfItems32;
// Add custom counter objects to CounterCreationDataCollection.
col.Add(counter1);
col.Add(counter2);
col.Add(counter3);
col.Add(counter4);
// delete the category if it already exists
if(PerformanceCounterCategory.Exists("MyAspNetThreadCounters"))
{
PerformanceCounterCategory.Delete("MyAspNetThreadCounters");
}
// bind the counters to the PerformanceCounterCategory
PerformanceCounterCategory category =
PerformanceCounterCategory.Create("MyAspNetThreadCounters",
"", col);
}
}
Compile the Console Application
At a command prompt, use the following command line to compile your code.
csc.exe /out:CreateAspNetThreadCounters.exe /t:exe /r:system.dll
CreateASPNETThreadCounters.cs
Run AspNetThreadCounters.exe
To run the console application, run the following.
CreateAspNetThreadCounters.exe
Results
When you run CreateAspNetThreadCounters.exe, the following output is produced.
MyAspNetThreadCounters performance counter category is created. [Press Enter]
Use Regedt32.exe to validate that your performance counter category and your custom performance counter are created beneath the following registry location.
HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services
MyAspNetThreadCounters is the name of the performance counter category and the counter names include Available Worker Threads, Available IO Threads, MaxWorker Threads, and Max IO Threads.
Create an ASP.NET Application to Refresh the Counters
To refresh the counters, you must retrieve information about the ASP.NET thread pool, and for that, you must run code from within an ASP.NET application.
Create an ASP.NET Application
Follow these steps to create the application.
To create the ASP.NET application
Create a new folder named C:\InetPub\wwwroot\AspNetThreadPoolMonitor\.
Use Internet Services Manager to mark this folder as an application and to create a virtual directory.
Create the following three files in the folder: Global.asax, Sleep.aspx, and StartWebApp.aspx.
Global.asax
<%@ Application Language=C# %> <%@ import namespace="System.Threading" %> <%@ import namespace="System.Diagnostics" %> <script runat=server> public bool MonitorThreadPoolEnabled = true; protected void Application_Start(Object sender, EventArgs e) { Thread t = new Thread(new ThreadStart(RefreshCounters)); t.Start(); } public void RefreshCounters() { while (MonitorThreadPoolEnabled) { ASPNETThreadInfo t = GetThreadInfo(); ShowPerfCounters(t); System.Threading.Thread.Sleep(500); } } protected void Application_End(Object sender, EventArgs e) { MonitorThreadPoolEnabled = false; } public struct ASPNETThreadInfo { public int MaxWorkerThreads; public int MaxIOThreads; public int MinFreeThreads; public int MinLocalRequestFreeThreads; public int AvailableWorkerThreads; public int AvailableIOThreads; public bool Equals(ASPNETThreadInfo other) { return ( MaxWorkerThreads == other.MaxWorkerThreads && MaxIOThreads == other.MaxIOThreads && MinFreeThreads == other.MinFreeThreads && MinLocalRequestFreeThreads == other.MinLocalRequestFreeThreads && AvailableWorkerThreads == other.AvailableWorkerThreads && AvailableIOThreads == other.AvailableIOThreads ); } } public ASPNETThreadInfo GetThreadInfo() { // use ThreadPool to get the current status int availableWorker, availableIO; int maxWorker, maxIO; ThreadPool.GetAvailableThreads( out availableWorker, out availableIO); ThreadPool.GetMaxThreads(out maxWorker, out maxIO); ASPNETThreadInfo threadInfo; threadInfo.AvailableWorkerThreads = (Int16)availableWorker; threadInfo.AvailableIOThreads = (Int16)availableIO; threadInfo.MaxWorkerThreads = (Int16)maxWorker; threadInfo.MaxIOThreads = (Int16)maxIO; // hard code for now; could get this from machine.config threadInfo.MinFreeThreads = 8; threadInfo.MinLocalRequestFreeThreads = 4; return threadInfo; } public void ShowPerfCounters(ASPNETThreadInfo t) { // get an instance of our Available Worker Threads counter PerformanceCounter counter1 = new PerformanceCounter(); counter1.CategoryName = "MyAspNetThreadCounters"; counter1.CounterName = "Available Worker Threads"; counter1.ReadOnly = false; // set the value of the counter counter1.RawValue = t.AvailableWorkerThreads; counter1.Close(); // repeat for other counters PerformanceCounter counter2 = new PerformanceCounter(); counter2.CategoryName = "MyAspNetThreadCounters"; counter2.CounterName = "Available IO Threads"; counter2.ReadOnly = false; counter2.RawValue = t.AvailableIOThreads; counter2.Close(); PerformanceCounter counter3 = new PerformanceCounter(); counter3.CategoryName = "MyAspNetThreadCounters"; counter3.CounterName = "Max Worker Threads"; counter3.ReadOnly = false; counter3.RawValue = t.MaxWorkerThreads; counter3.Close(); PerformanceCounter counter4 = new PerformanceCounter(); counter4.CategoryName = "MyAspNetThreadCounters"; counter4.CounterName = "Max IO Threads"; counter4.ReadOnly = false; counter4.RawValue = t.MaxIOThreads; counter4.Close(); } </script>
Sleep.aspx
<%@ Page language="C#" %> <script runat=server> void Page_Load(Object sender, EventArgs e) { Response.Write("Sleep"); System.Threading.Thread.Sleep(30000); } </script>
StartWebApp.aspx
<%@ Page language="C#" %> <script runat=server> void Page_Load(Object sender, EventArgs e) { Response.Write("This ASP.NET application has started.<br>"); Response.Write("You can now close this page."); } </script>
Start the ASP.NET Application
Start your ASP.NET application by opening Microsoft Internet Explorer and browsing to the following page.
https://localhost/AspNetThreadPoolMonitor/StartWebApp.aspx
View the Counters in Performance Monitor
Use the Performance Monitor tool to view the counters.
To view the counters in Performance Monitor
- At a command prompt, type perfmon.exe, and then press Enter.
- On the toolbar, click New Counter Set. (If the NewCounterSet button is disabled, you already have a new counter set.)
- On the toolbar, click Add.
- In the Add Counters dialog box, for Performance object, click MyASPNetThreadCounters.
- In the Select counters from this list box, click Available IO Threads, and then click Add.
- In the Select counters from this list box, click Available Worker Threads, and then click Add.
- In the Select counters from this list box, click Max IO Threads, and then click Add.
- In the Select counters from this list box, click Max Worker Threads, and then click Add.
- Click Close.
- On the toolbar, click Properties.
- In the System Monitor Properties dialog box, click the Graph tab.
- On the Graph tab, set Maximum for the Vertical scale to 20.
- Click OK.
**Note **If the counters show zero values, the ASP.NET application is not running.
Run a Test Page that Uses Threads
The Sleep.aspx test page can be used to keep an ASP.NET I/O thread busy. Open multiple instances of your browser and, in each instance, open the Sleep.aspx page. In Performance Monitor, you can see the number of available worker or I/O threads changing, depending on your scenario. For example, if you do not have the hotfix mentioned in Microsoft Knowledge Base article 816829, "FIX: When I/O Thread Processes a Slow Request, Completions on Named Pipes Between Inetinfo.exe and Aspnet_wp.exe Are Blocked," at https://support.microsoft.com/default.aspx?scid=kb;EN-US;816829, then only I/O threads change and not the worker threads.
Additional Resources
For more information, see the following resources:
- For a printable checklist, see "Checklist: ASP.NET Performance" in the "Checklists" section of this guide.
- Chapter 6, "Improving ASP.NET Performance"
- For more information about the thread pool class, see "ThreadPool Class" in .NET Framework Class Library on MDSN® at https://msdn.microsoft.com/en-us/library/system.threading.threadpool(VS.71).aspx.
- For more information about the <processModel> element, see "<processModel> Element" in .NET Framework General Reference on MSDN at https://msdn.microsoft.com/en-us/library/7w2sway1(VS.71).aspx.
Retired Content |
---|
This content is outdated and is no longer being maintained. It is provided as a courtesy for individuals who are still using these technologies. This page may contain URLs that were valid when originally published, but now link to sites or pages that no longer exist. |