Modifying the .NET CLR ThreadPool Settings for ASP.NET 4.x
Published Apr 17 2019 01:39 PM 15K Views
Microsoft

One common problem we see is the blocking of .NET ThreadPool threads. This blocking can lead to ThreadPool thread starvation, and sometimes even deadlocks.

This is especially true as of late with more and more folks using asynchronous code, and trying to convert existing, synchronous methods into async, but leaving parts of the code sync.

I don't go into the above scenarios in this post; but, some folks want to change the ThreadPool defaults regarding the number of maximum or minimum worker and/or IO threads to troubleshoot a problem or test application behavior with different settings. There is conflicting information out there on how to change the ThreadPool settings for an ASP.NET 4.x web app, so the purpose of this post is to cover how to make those changes correctly.

 

NOTE: We generally don't recommend modifying the CLR TP thread counts, as the defaults work for the vast majority of scenarios.

 

If you want to change the ThreadPool settings in an ASP.NET application, you have three (3) choices:

  1. Modify the system.web\processModel element in the machine.config. There could be several copies of this file on a typical server. For the most part, these are the ones we deal with:
    • C:\Windows\Microsoft.NET\Framework[64]\v2.0.50727\CONFIG\machine.config
    • C:\Windows\Microsoft.NET\Framework[64]\v4.0.30319\CONFIG\machine.config
  2. Use ThreadPool.SetMaxThreads and ThreadPool.SetMinThreads from within the application itself
  3. If using IIS 10 (Windows Server 2016 and up), then you can set an environment variable specific to the application pool that is hosting the application. 

1 - machine.config

For #1, here's how the processModel section is declared in the machine.config:

            <section name="processModel" type="System.Web.Configuration.ProcessModelSection, System.Web, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" allowDefinition="MachineOnly" allowLocation="false"/>

 

allowDefinition="MachineOnly" means this section/element can only be configured in the machine.config, and not any other downlevel config. The allowLocation="false" means this section cannot be placed in a <location> tag.

This means if you try to change the processModel element in an app's web.config or try to modify a copy of one inside a location tag, you'll receive an error. 

 

A modifed processModel element could look like this:
<processModel autoConfig="false" maxWorkerThreads="100" minWorkerThreads="2" maxIoThreads="100" minIoThreads="2" />

 

For any explicit thread counts to be honored, you'd need to set autoConfig="false" - by default it's set to true.

 

Some folks have tried placing a modified processModel element into a separate .config file, then configuring the IIS host's application pool's CLRConfigFile property to load that file. (This feature is described here.) This does not work for modifying the .NET ThreadPool, and will not modify the actual <processModel> settings that get pulled-in.

This also means modifying the Aspnet.config file that exists in the Framework[64]\[version]\ directory to add a system.web\processModel element will not work. We are limited to changing this element in the machine.config only.

But wait, doesn't editing the machine.config affect the entire server? Yes, it does! Granted, we're only editing the system.web section, so only apps that consume that section will be modified. Still, this is a reason to not make the change here, even though you can.

 

2 - Code

You can make this change anywhere in the app (even on an aspx page or an MVC View, for example) and it will stick for the rest of the app's lifetime, or until it gets changed again. When the application pool/domain/etc. is recycled, it will pull the defaults until your call to modify the TP is made again.

One idea is to use the app's *.config (or wherever you're storing app settings) to store your desired changes, then pull those values into the app and apply them early-on in the application's lifetime, such as during application startup. 

 

3 - IIS 10 Environment Variable

The environment variable that will set the minWorkerThreads for .NET Framework is this: COMPlus_ThreadPool_ForceMinWorkerThreads

This can be set on the machine as a whole, but IIS 10 gained the ability to set environment variables per application pool. This will affect every application hosted in the pool, but it's better than affecting every application on the machine.

There are a few different ways to set app pool environment variables in IIS, so here are two of them:

  1. via IIS Manager's Configuration Editor, or
  2. via the appcmd.exe

Here's the docs page for the configuration element:

https://learn.microsoft.com/en-us/iis/configuration/system.applicationHost/applicationPools/add/envi...

 

Note: when setting this environment variable, the value is read by .NET as hexadecimal

 

In IIS Manager, navigate to the server/root node, then:

Configuration Editor -> Section: system.applicationHost/applicationPools -> Click the (Collection) field and click the little "..." button that appears -> select the desired app pool in the list -> select "environmentVariables" and open that collection via the "..." button:

Name: COMPlus_ThreadPool_ForceMinWorkerThreads

Value: ##

 

When using appcmd.exe, here are some example commands - note you would need to change the indicated portions:

To add the env var if it does not exist:
C:\windows\system32\inetsrv\appcmd.exe set config -section:system.applicationHost/applicationPools /+"[name='DefaultAppPool'].environmentVariables.[name='COMPlus_ThreadPool_ForceMinWorkerThreads',value='64']" /commit:apphost

 

To modify the env var if it does exist:
C:\windows\system32\inetsrv\appcmd.exe set config -section:system.applicationHost/applicationPools /[name='DefaultAppPool'].environmentVariables.[name='COMPlus_ThreadPool_ForceMinWorkerThreads'].value:"96" /commit:apphost

 

To remove the env var:
C:\windows\system32\inetsrv\appcmd.exe set config -section:system.applicationHost/applicationPools /-"[name='DefaultAppPool'].environmentVariables.[name='COMPlus_ThreadPool_ForceMinWorkerThreads']" /commit:apphost

 

 

Again, we don't recommend modifying the ThreadPool counts unless absolutely necessary. This post was just to show how to make the changes so they stick.

6 Comments
Co-Authors
Version history
Last update:
‎Aug 08 2023 09:14 PM
Updated by: