Knowing how thread injection works in .Net is key to allowing your ASP.Net application to make the best use of system resources Credit: Thinkstock In the .Net Framework, the CLR is responsible for meting out resources to running applications. In particular, the CLR thread pool determines when threads are to be added or taken away. Understanding how this works will help you determine how to configure your ASP.Net application for optimal performance. The CLR thread pool contains two kinds of threads—the worker threads and the I/O completion port or IOCP threads. That means your ASP.Net worker process actually contains two thread pools: the worker thread pool and the IOCP thread pool. Naturally, these pools have different purposes. When you use methods like Task.Run, TaskFactory.StartNew, and ThreadPool.QueueUserWorkItem, the runtime takes advantage of worker threads for processing. When you make asynchronous I/O calls in your application, or your application accesses the file system, databases, web services, etc., then the runtime uses IOCP threads. Note too that each application domain has its own thread pool. Let’s take a closer look at how these threads are created and removed in the .Net Framework. Thread injection strategies The .Net thread pool starts injecting new threads whenever the number of busy threads becomes equal to the number of configured minimum threads in the thread pool. The default value of the minimum setting, which is the minimum number of both worker and IOCP threads, is determined by the number of processors in your system. Hence, if your system has four cores, you would have four worker threads and four IOCP threads by default. The .Net thread pool then injects additional worker threads on demand if existing threads are utilized and there is still work to be done. By the same token, if the demand for resources falls, the thread pool will begin taking threads away. Executing the following code snippet would display the number of logical processors in your system and the minimum number of worker and IOCP threads available. static void Main(string[] args) { int minimumWorkerThreadCount, minimumIOCThreadCount; int logicalProcessorCount = System.Environment.ProcessorCount; ThreadPool.GetMinThreads(out minimumWorkerThreadCount, out minimumIOCThreadCount); Console.WriteLine(“No. of processors: “ + logicalProcessorCount); Console.WriteLine(“Minimum no. of Worker threads: “ + minimumWorkerThreadCount); Console.WriteLine(“Minimum no. of IOCP threads: “ + minimumIOCThreadCount); Console.Read(); } The .Net thread pool manages threads using its built-in heuristics. The strategies adopted include starvation avoidance and a hill-climbing algorithm. In the former case, the .Net thread pool continues to add worker threads if there is no visible progress on the queued items. In the latter case, the .Net thread pool tries to maximize the throughput using as few threads as possible. The .Net thread pool injects or removes threads at intervals of 500 milliseconds or as a thread becomes free, whichever comes first. Now, based on the feedback available to the runtime, the .Net thread pool either removes threads or adds threads to maximize the throughput. If adding a thread does not increase throughput, it takes a thread away. This is the CLR’s hill-climbing technique in action. Now suppose you are running your ASP.Net application on IIS and your web server has a total of four CPUs. Assume that at any given point in time, there are 24 requests to be processed. By default the runtime would create four threads, which would be available to service the first four requests. Because no additional threads will be added until 500 milliseconds have elapsed, the other 20 requests will have to wait in the queue. After 500 milliseconds have passed, a new thread is created. As you can see, it will take many 500ms intervals to catch up with the workload. This is a good reason for using asynchronous programming. With async programming, threads aren’t blocked while requests are being handled, so the four threads would be freed up almost immediately. Recommended thread settings Given the way the .Net thread pool works and what we have discussed thus far, it is strongly recommended that you change the minimum configuration value—the default value—for both worker and IOCP threads. To do this in ASP.Net, you should change the minWorkerThreads and minIoThreads configuration settings under the <processModel> configuration element in the machine.config file in your system. <configuration> <system.web> <processModel minWorkerThreads=”provide your desired value here” minIoThreads=”provide your desired value here” /> </system.web> </configuration> You can set the minimum configuration values for both worker and IOCP threads to any value between one and 50. A good approach is to take a user mode process dump of the IIS worker process (W3wp.exe) and then use the !threadpool command to report the total number of worker threads. Once you know this value, simply divide it by the number of processor cores on your system to determine the minimum worker and IOCP thread settings. For example, if the total count of worker threads is 100 and you have four processors in your system, you can set the minimum values for both worker and IOCP threads to 25. To change the default minimum thread settings outside of ASP.Net, you can use the ThreadPool.SetMinThreads() method. With the goal of better thread management and improved performance, the CLR thread pool has been improved with each version of the CLR. As an example, with .Net Framework 4, the CLR gained thread stealing algorithms and support for concurrency and parallelism. With each new version of the CLR, the .Net thread pool is getting smarter about optimizing the throughput by creating and destroying threads as needed. In the meantime, you’ll want to experiment with different minimum thread settings to get the best performance from your .Net application. Related content feature What is Rust? Safe, fast, and easy software development Unlike most programming languages, Rust doesn't make you choose between speed, safety, and ease of use. Find out how Rust delivers better code with fewer compromises, and a few downsides to consider before learning Rust. By Serdar Yegulalp Nov 20, 2024 11 mins Rust Programming Languages Software Development how-to Kotlin for Java developers: Classes and coroutines Kotlin was designed to bring more flexibility and flow to programming in the JVM. Here's an in-depth look at how Kotlin makes working with classes and objects easier and introduces coroutines to modernize concurrency. By Matthew Tyson Nov 20, 2024 9 mins Java Kotlin Programming Languages analysis Azure AI Foundry tools for changes in AI applications Microsoft’s launch of Azure AI Foundry at Ignite 2024 signals a welcome shift from chatbots to agents and to using AI for business process automation. By Simon Bisson Nov 20, 2024 7 mins Microsoft Azure Generative AI Development Tools news Microsoft unveils imaging APIs for Windows Copilot Runtime Generative AI-backed APIs will allow developers to build image super resolution, image segmentation, object erase, and OCR capabilities into Windows applications. By Paul Krill Nov 19, 2024 2 mins Generative AI APIs Development Libraries and Frameworks Resources Videos