DEV Community

G.L Solaria
G.L Solaria

Posted on • Edited on

C# Async Await, Eventually: Thread Pools

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). 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!

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

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 ...

[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>(); } } } 
Enter fullscreen mode Exit fullscreen mode

I used https://github.com/dotnet/BenchmarkDotNet to produce the following bar plot ...
Starting threads can take some time
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.

Top comments (0)