top of page

Multithreading and parallelism with C # examples in the .NET platform.




What is multithreading?

Multithreading— it is the ability of a platform (application) to perform multiple operations in one process. One process cannot execute more than one thread at a one-time unit.


Real-life example. Imagine the situation in a restaurant in the kitchen with one chef in the kitchen. He runs from corner to corner, shreds cabbage, seasons salad with oil, puts the chicken in the oven, and receives wishes from customers from waiters. In this situation, the cook performs the function of process one in a multithreaded version. He cannot simultaneously understand chicken in the oven, receive orders, shreds cabbage without shifting his attention.

We can do the same in a single process.


What is Thread in .NET and how could we use it?


Thread— it is a programmatically allocated area in the processor core. This virtual implementation allows you to share kernel resources and work with two sequences of commands at once.

Let’s give an example of using the Thread itself.

Thread thread = new Thread(() => {
   Console.WriteLine("Hello from Thread!");
}); // create new Thread
thread.Start(); // start Thread that has been createdOutput: Hello from Thread!

The Thread class is featured in the advertisements for System.Threading and is a virtual thread in the system. You do not need to create threads yourself, since threads have already been created in the runtime environment. They are located in the ThreadPool and are waiting for the moment when the system needs to start using them. By itself, the operation of creating a new thread takes up time and resources.


Let’s try to create several Threads.

List<Thread> threads = new List<Thread>()
{
   new Thread(() => Console.WriteLine("Hello from Thread - 1!")),
   new Thread(() => Console.WriteLine("Hello from Thread - 2!")),
   new Thread(() => Console.WriteLine("Hello from Thread - 3!")),
   new Thread(() => Console.WriteLine("Hello from Thread - 4!")),
   new Thread(() => Console.WriteLine("Hello from Thread - 5!")),
};foreach(Thread thread in threads)
{
   thread.Start();
};Console.WriteLine("[MAIN THREAD] Hello From Main Thread!");

Console output looks like below:

Hello from Thread — 1! Hello from Thread — 5! Hello from Thread — 3! [MAIN THREAD] Hello From Main Thread! Hello from Thread — 2! Hello from Thread — 4!

We will output 6 different messages to the console, according to how much our runtime starts 6 different threads of execution (Main thread — the first thread that creates and runs another 5 already created threads).


If you pay attention, the output to the console is absolutely not in the order in which our threads are started. The reason for this is that threads are executed in a process in an arbitrary order based on priorities, each thread does its job without blocking other threads from executing. The Thread class allows us to set the priority of a thread (the available priorities are Highest, AboveNormal, Normal, BelowNormal, Lowest, the default is Normal), but this does not guarantee us 100% probability that threads are presented in the desired order. Threads that are created and used by users are Foreground (I will describe the difference between foreground and background streams in the next article).

thread.IsBackground; // false

In our case, all 6 are running in a single process.


Now that you are familiar with the basic concept of multithreading, I propose to consider an example of using parallelism.


What is parallelism?


Returning to our story with the chef, we remember how he was a poor fellow, he ran all over the kitchen and tried to grab onto various tasks. If we decided to give help to the turn of one more turn, then they would be able to divide their work among themselves. In this case, such work became parallel, as one of the cooks could chop the salad and the other deal with the chicken in the oven.


The .NET representative for all parallel operations is the Parallel class from the System.Threading.Tasks namespace.

Below is an example of running the code in parallel.

Parallel.Invoke(() => { 
  Console.WriteLine("Starting single thread in the 1st process...");
  Thread.Sleep(500); // Block current thread for 500 ms.
  Console.WriteLine("Finished single thread in the 1st process...");
}, () => {
  Console.WriteLine("Starting single thread in the 2nd process...");
  Thread.Sleep(500); // Block current thread for 500 ms.
  Console.WriteLine("Finished single thread in the 2nd process...");
});

Console output looks like below:

Starting single thread in the 1st process… Starting single thread in the 2nd process… Finished single thread in the 2nd process… Finished single thread in the 1st process…

Let’s dive into the code above. The Parallel.Invoke method takes an array of delegates as arguments to Actions[]. Each of the passed Action delegates will be executed in a separate process (if exists), and within each separate process, one separate thread will be launched. At the same time, you do not need to manually launch them yourself. The runtime itself will take the threads previously created in the ThreadPool and they will execute each of these pieces of code in separate processes.

Remembering the situation with a single turn in the kitchen, you remember that he cannot simultaneously perform two actions at once. The same applies to our devices. Unable to execute code on a single-core device. The number of processes on a device can be no more than the number of cores on the same device.


Conclusion.

Both of these concepts cause problems for novice developers. In the next article, we’ll dive deeper into multithreading and see how it works in .NET, how it interacts, and more.



Source: Medium - Serhii Chyzhyk


The Tech Platform

0 comments

Recent Posts

See All
bottom of page