The series so far:
- The Unconventional Guide to Introduction to Thread
- Why Do People Think Dedicated CLR Threads is a Good Idea?
- How Not Knowing Thread Members and Execution State Makes You a Rookie
- Doing Thread Scheduling and Priority the Right Way
- How to Start Using CLR's Thread Pool
- What Wikipedia Can't Tell You About Thread Execution Contexts
- The Insider's Guide to Cooperative Cancellation and Timeout
Introduction
Thread pool is a set of threads that are available for your application’s use.
- CLR contains code to manage own Thread Pool
- One Thread Pool per CLR and shared by all AppDomains
Note: Threadpool threads are background by default
Working of Threadpool Threads
- As tasks arrive they are placed on a queue.
- Threads on the Threadpool grab the next available task on the queue.
Threadpool thread kiling spree
When a threadpool thread has been idle with nothing to do for some period of time, the thread wakes itself up and kill itself to free up resources.
- Time to kill after idle depends on CLR.
- As the thread is killing itself, there is a performance hit.
- However, this probably doesn’t matter, because the thread is killing itself, because it has been idle, which means that your application isn’t performing a lot of work.
Managing Worker Threads
- Global Queue is used to manage the items with FIFO
- If Worker threads enqueue work items they have their local queue for that and have access to head only LIFO. Hence Reversed execution.
- Steal from other Local Queue, if local empty
- Finally from Global Queue, if all empty
- If Global Empty, Sleep Waiting
- If Sleep Waiting for long, kills it
Note: Windows Scheduler determines which thread to schedule first
Demo
Performing a simple Compute-Bound Operation using Thread Pool. Please found the running version here.
//Compiler version 4.0.30319.17929 for Microsoft (R) .NET Framework 4.5
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.RegularExpressions;
// Threading namespace
using System.Threading;
namespace Rextester {
public class Program {
public static void Main(string[] args) {
//Your code goes here
Console.WriteLine("Hello, world!");
DoWork();
}
public static void DoWork() {
// Queue a task.
System.Threading.ThreadPool.QueueUserWorkItem(
new System.Threading.WaitCallback(SomeLongTask));
// Queue another task.
System.Threading.ThreadPool.QueueUserWorkItem(
new System.Threading.WaitCallback(AnotherLongTask));
System.Threading.Thread.Sleep(5000);
}
private static void SomeLongTask(Object state) {
Console.WriteLine("Long Task picked up by " + Thread.CurrentThread.ManagedThreadId);
// Insert code to perform a long task.
Console.WriteLine("Long Task Started");
System.Threading.Thread.Sleep(1000);
Console.WriteLine("Long Task Ended");
}
private static void AnotherLongTask(Object state) {
Console.WriteLine("Another Long Task picked up by " + Thread.CurrentThread.ManagedThreadId);
// Insert code to perform a long task.
Console.WriteLine("Another Long Task Started");
System.Threading.Thread.Sleep(500);
Console.WriteLine("Another Long Task Ended");
}
}
}