Multithreading and parallelism with C # examples in the .NET platform. Minimized simple description.
Part 2.
Multithreading. Threads and ThreadPool

Serhii Chyzhyk
4 min readOct 9, 2021

--

Greetings to all who are not indifferent to the topic of multithreading and parallelism. This time we will dive a little deeper and see how to use this on the .NET platform. This article is a continuation of a series of articles on this topic.
A link to the introductory article can be found here.

So, let's go!

Class Thread. Deep dive
As mentioned earlier, the Thread class is a virtual thread that can perform part of the actions within a certain process.
The Thread class has the following 4 constructors:


public Thread(ParameterizedThreadStart start);
public Thread(ThreadStart start);public Thread(ParameterizedThreadStart start, int maxStackSize);public Thread(ThreadStart start, int maxStackSize);

You probably can’t wait to find out what it is and what it is eaten with. Let’s go in order.
ParameterizedThreadStart is a kind of delegate that allows us to pass our parameters to the thread when it starts up.
Example:

ParameterizedThreadStart parameterizedThreadStart = 
(object value) => Console.WriteLine(value);
Thread thread = new Thread(parameterizedThreadStart);
thread.Start(“Write this message in the thread!”);

Console out:

Write this message in the thread!

ThreadStart is also a delegate that is executed when the thread starts. Unlike ParameterizedThreadStart, a thread does not take any parameters during startup.
Example:

ThreadStart threadStart = () => Console.WriteLine(“Example!”);
Thread thread = new Thread(threadStart);
thread.Start();

Console out:

Example!

If we try to pass a parameter to our thread, we will get an exception when running such a code.

ThreadStart threadStart = () => Console.WriteLine(“Example!”);
Thread thread = new Thread(threadStart);
thread.Start("Test string param"); // InvalidOperationException

What is ThreadPool?

The official description of Microsoft looks like below:
The System.Threading.ThreadPool class provides your application with a pool of worker threads that are managed by the system, allowing you to concentrate on application tasks rather than thread management. If you have short tasks that require background processing, the managed thread pool is an easy way to take advantage of multiple threads. Use of the thread pool is significantly easier in Framework 4 and later, since you can create Task and Task<TResult> objects that perform asynchronous tasks on thread pool threads.

Well, if in simple words, then inside the process there is already a certain place where threads have been created in advance, which at any time are ready to perform our tasks without the need to create them.
An example of how to do this:

WaitCallback waitCallback = 
(object? state) => Console.WriteLine(state);
ThreadPool.QueueUserWorkItem(waitCallback,
“Use Thread from ThreadPool!”);
Console.ReadKey();

I think it’s nothing complicated, but it’s worth explaining how it works.

ThreadPool works diagram

We have a certain place where all tasks within the process go (queue), then the system takes a new thread from the ThreadPool and executes the task in it.
So, in our case, the ThreadPool.QueueUserWorkItem() method allows us to set our task to a queue and when it runs, a thread from the thread pool will be assigned to it, in which it will be successfully executed.
A more illustrative example:

for (int i = 0; i < 20; i++)
{
int stateNumber = i;
// Provides posibility ofusage existing threads from ThreadPool instead of direcly creation a new Thread instance
ThreadPool.QueueUserWorkItem(state => {
Console.WriteLine("Doing work: {0}", stateNumber);
Thread.Sleep(500);// blocks every Thread from ThreadPool
Console.WriteLine("Work finished: {0}", stateNumber);
});
);
}
Console.ReadKey();

Run this code and see how our tasks are executed on the thread pool one by one. The console output will be as follows:

Doing work: 1
Doing work: 7
Doing work: 5
Doing work: 4
Doing work: 0
Doing work: 2
Doing work: 3
Doing work: 6
Work finished: 7
Work finished: 0
Doing work: 9
Work finished: 3
Doing work: 10
Work finished: 4
Doing work: 11
Work finished: 2
Doing work: 12
Work finished: 6
Doing work: 13
Work finished: 1
Doing work: 14
Work finished: 5
Doing work: 15
Doing work: 8
Work finished: 14
Work finished: 8
Doing work: 17
Work finished: 10
Doing work: 18
Work finished: 11
Doing work: 19
Work finished: 12
Doing work: 16
Work finished: 15
Work finished: 9
Work finished: 13
Work finished: 16
Work finished: 17
Work finished: 18
Work finished: 19

As you can see, the order of execution is not the same in which we added our tasks to the queue, which once again clearly demonstrates to us and confirms the statement about how this is implemented and can be used.

And also, guys, remember this thing. The Task class is not a thread, but just a unit of an asynchronous operation. Further, I will definitely tell you what it is, how it works, and how we can use it for our own purposes and those in need.

Conclusion.
Multithreading allows you to break up a single problem by solving it faster within the same process.

--

--

Serhii Chyzhyk

Software Engineer 🚀 | Navigating code, crafting tech solutions