C# Async Await, Eventually: Thread Pools
Tags: csharp, async
Published: 2021-07-03 21:10:06

In my previous post in the series I covered what Asynchronous Programming is and introduced threads ([https://dev.to/glsolaria/c-async-await-eventually-asynchronous-programming-5h46](https://dev.to/glsolaria/c-async-await-eventually-asynchronous-programming-5h46)). Now I am going to eventually get to an explanation of what the Synchronization Context (now don't be scared) is but to do that I need to answer a different question.

Trust me I will bring this together and get to Async/Await eventually and hopefully you will be in a better position to understand what is happening under the hood.

![Hopefully I won't end up like this!](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/vmz4y9jvq86nqjkhetlg.jpg)

# What is the Thread Pool?
## Simply: What is the Thread Pool?
Threads are resource intensive critters. It takes a relatively long time to create them. By creating a pool of them, the Thread Pool has a ready set of threads to use to execute your code. 
### Humour me with a Thread Pool analogy ...
You asked for it so here I go! Imagine you have pool of secretaries that can do work for you: photocopy, file, answer phones etc. Getting a new secretary takes time to advertise, interview, then hire so you decide to have a set already ready to go. When the work comes in, you allocate a secretary to do the work and then the secretary returns to the pool waiting for the next job. (The pool could be a swimming pool or a pool table but basically it's where the secretaries hang out waiting for new work). In this analogy you are running a Secretary Pool. Just replace secretary with Thread and hopefully you can see where I am going with this.
![Again I hope I am not missing the mark](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/2uxv5ylthqrnexh72eec.png)
## More complicated: What is the Thread Pool?
Threads are resource intensive critters. It takes a while to start them because it involves allocating resources and may involve communication between the kernel space and the runtime. I have included the following code for your reference but I really want you to focus on the bar plot this code produces ...
```csharp
[RPlotExporter]
[SimpleJob(RunStrategy.ColdStart, RuntimeMoniker.Net50, baseline: true)]
public class ThreadPoolBaseliner
{
  private Thread[] simpleThreadPool; 

  [Params(1, 2, 4, 8, 16, 32, 64, 128)] public int N;

  [IterationSetup]
  public void Setup() => simpleThreadPool = new Thread[N];
 
  [Benchmark]
  public void ThreadCreation() => CreateThreads(N);

  [Benchmark]
  public void ForLoopAddition() => LoopAdditionFor(N);

  public void CreateThreads(int numberOfThreads)
  {
      for (int ii = 0; ii < numberOfThreads; ++ii)
      {
        simpleThreadPool[ii] = new Thread(() => 
          { Thread.Sleep(0); });
      }
  }

  public void LoopAdditionFor(int numberOfIterations)
  {
      int total = 0;
      for (int ii = 0; ii < numberOfIterations; ++ii) total += 42;
  }

  private class Program
  {
      public static void Main(string[] args)
      {
        var summary = BenchmarkRunner.Run<ThreadPoolBaseliner>();
      }
  }
}
```
I used [https://github.com/dotnet/BenchmarkDotNet](https://github.com/dotnet/BenchmarkDotNet) to produce the following bar plot ...
![Starting threads can take some time](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/nm7pf47dvkxdpjqnla7w.png)
The time is shown in microseconds and the plot compares the benchmark results between a simple for loop and thread creation where the number of iterations starts at 1 then doubles until we get to 128. Hopefully you can see that starting threads can be a costly exercise.

So in conclusion, a Thread Pool manages thread creation, execution, and destruction in such a way to balance the resource costs against the benefits of asynchronous execution.