Asynchronous and Parallel Programming in C# .NET

Thanh Le
Geek Culture
Published in
9 min readApr 24, 2020
Photo by Alexandre Debiève on Unsplash

Context

Last week, I had a very long discussion with my co-workers around Asynchronous and Parallel Programming during coffee time. Although that is a very old topic, I believe that there are still a lot of confusions between them. That is the reason why I decided to write this article.

Both Asynchronous and Parallel Programming enable us to finish our tasks faster. But how many of you still think that they are the same thing and just different terms?

Let’s walk through this article to find the answer for the question above as well as other questions like “what they are?” and “how to use them in the real example as well as in a .NET Application?”

Parallel Programming

Nowadays, multicore processors (2 cores, 4 cores, 8 cores …) become very popular. Even they can be found in most devices. (you can open Task Manager to see how many cores does your computer have)

figure 1: computer with 6 cores

However, many developers are not using the advantage of having multicore processors to develop better software applications. They follow the same as what 90s’ developers did: creating single-threaded applications. In other words, they don’t take advantage of all the extra processing power. Imagine you have many features have to deliver and have many developers who are ready to implement these features. But you only assign to one developer to implement all features while others are available. It’s not efficient!

I will bring to you an example in real life.

Photo by Nathan Dumlao on Unsplash

Let’s imagine, you have a restaurant. Several years ago, when you started, you only had a small amount of money and didn’t enough money for hiring other staffs. You were a chef, a waiter and a cashier (obviously a manager :) ). As a result, you could only serve one customer at one time. You tried your best to serve more customers in a day but you couldn’t. By the time, you started hiring other staffs from the waiter, the chef and then the cashier. Now, your restaurant has 3 staffs except you and the number of customers your restaurant can serve per day is increased approximately triple.

What exactly is Parallel Programming?

image source: https://computing.llnl.gov/tutorials/parallel_comp/

In very simple terms, it is the use of multicore processors (even multiple machines) to execute a task. This type of programming takes a task, breaks it down into a series of smaller ones, delivers instructions, and processors execute the solutions at the same time. Many computers and devices, such as laptops, desktops, mobile phone and tablet, use this programming in their hardware to ensure that tasks are quickly completed in the background.

Disadvantages

There are a few disadvantages of using Parallel Programming. The first, that might be difficult to learn. The programming that targets parallel architectures can be overwhelming at first, so it does take time to fully understand. Additional, code tweaking is not straightforward and must be modified for different target architectures to properly improve performance. Finally, power consumption is another problem; a variety of cooling technologies will be required in order to cool your computer.

Asynchronous Programming

Before talking about Asynchronous, let’s talk about Synchronous first. Let’s jump back to the “Restaurant” example above.

Photo by Jay Wennington on Unsplash

Recently, so many customers complain about the quality service of your restaurant. As a restaurant manager, you did some investigations and realized that normally customer used to order a drink (coke or beer), starter (salad), main (beefsteak) and dessert (tiramisu) when they visited your restaurant. But the problem here is they have to wait until everything in their order is ready (even drink they have to wait) before they can start eating. That is Synchronous. In order to bring a better experience to the customer, you asked your staffs that once a dish is ready, then it should be brought to the customer intermediately. Obviously, that made the customer more satisfied. That is Asynchronous.

Back to programming!

With Synchronous your application will run all tasks in sequence. In other words, you fire the execution of each task and then wait until it finishes before firing the next one.

figure 2: Synchronous

As a result, applying Synchronous might stop the user interface (UI) thread. Typically, these applications that have only one UI thread, and when you use it as a blocking operation, you will get the spinning wheel (and “not responding” in the caption title) in your program — not the best experience for your users. There’s no sense sitting there waiting for something when you could let the other task can run along on another processor core and let you know when it’s done as it doesn’t use the multicore processors.

In contrast, when executing asynchronously, the program doesn’t run all tasks in sequence: it fires the tasks and then waits for their end.

figure 3: Asynchronous

Asynchronous eliminates disadvantages of Synchronous. It won’t hang the UI thread (because it can run as a background task), and it can use all the cores in your machine and make better use of machine resources.

Disadvantages

  • You must synchronize tasks. Say that in Figure 3 you run a task that must be executed after the other four have finished. You will have to create a mechanism to wait for all tasks to finish before launching the new task.
  • You must address concurrency issues. If you have a shared resource, like a list that is written in one task and read in another, make sure that it’s kept in a known state.
  • The program logic is completely scrambled. There is no logical sequence anymore. The tasks can end at any time, and you don’t have control of which one finishes first.

Are Asynchronous and Parallel Programming the same thing?

Photo by Jørgen Håland on Unsplash

Visual Studio magazine defines ‘Asynchronous Programming’ as “… a means of parallel programming in which a unit of work runs separately from the main application thread and notifies the calling thread of its completion, failure or progress.” I believe this definition is somewhat limiting as it could be true in the .NET world. Consider Javascript, for example, Javascript is a single-threaded language by definition. Yet you can still use asynchronous patterns (such as callbacks, promises, or async/await). The way that javascript interpreters handle this, is by executing code in a single event loop. There is a detailed explanation of this here.

Implement Asynchronous and Parallel Programming in C# .NET

Both Asynchronous and Parallel Programming are not new in C# .NET. The Asynchronous Programming Model (APM) is the oldest model in .NET and has been available since version 1.0. Because it was complicated to implement, Microsoft introduced a new model in .NET 2.0: the Event-Based Asynchronous Pattern (EAP). EAP simplified things, but it wasn’t enough. So in .NET 4.0, Microsoft implemented a new model: the Task Parallel Library (TPL). The TPL is a huge improvement over the previous models. It simplifies parallel processing and makes better use of system resources. With TPL we can implement Parallel Programming in C# .NET very easy.

Async and Await keywords were introduced in C# 5.0 by Microsoft. When you use the “Async” keyword, you can write code the same way you wrote synchronous code. The compiler takes care of all the complexity and frees you to do what you do best: writing the logic. There are some rules for writing the Async method:

  • The method signature must have the async keyword.
  • The method name should end with Async (this is not enforced, but it is a best practice).
  • The method should return Task, Task<T>, or void.

To use this method, you should wait for the result (i.e., use the await method). Following these guidelines, when the compiler finds an await method, it starts to execute it and will continue the execution of other tasks. When the method is complete, the execution returns to its caller.

It’s time to practice!

Photo by Jordan Sanchez on Unsplash

Now, I have a C# example to find the prime numbers from 2 to 10,000,00 using Synchronous Programming as below:

Note: I copied the logic to find Prime Number by “googling”, so don’t focus too much about the algorithm. In case you have a better algorithm to find Prime Number, please send it via response.

And here is the result

figure 4: find prime number synchronously

Total prime numbers: 664579

Processing time: 5895 milliseconds

Let’s try to implement Parallel Programming by using TPL

As you can see, with Parallel Programm, I divided the list from 2 to 10,000,000 into 10 parts and run parallel

figure 5: running system with 10 parts

Here is the result

figure 6: find prime number parallel

Total prime numbers: 664579

Processing time: 1757 milliseconds (much better)

I tried to update “numParts” to 20, but it seems to be my computer cannot run faster as the processing time is 1737. — My computer processor is 2.2 GHz Quad-Core Intel Core i7.

Now, let’s try with Asynchronous

Notice that there is a new ProcessPrimesAsync method. When you use await in a method, it must be marked as async.

Note: Before C# 7.1, you could not mark Async for Main method. In order to apply Asynchronous your code should look like this:

figure 7: before C# 7.1

When Main executes the method, without the await keyword, it starts it but doesn’t wait for finish. For that reason, we need a command “Console.ReadLine” (or the program would end before the execution).

Do you know why I have to create 10 tasks?

figure 7: 10 async tasks

Because, if I only have one task then the application still need to wait until this task is finished. That is the same with Synchronous.

And here is the result(running with 10 tasks):

figure 8: find prime number async

Total prime numbers: 664579

Processing time: 1987 milliseconds (not bad)

You can find the source code for the example here:

Conclusion

In this article, we already walked through the basic definition of Asynchronous and Parallel Programming as well as made an example together. But there are more things that you should know about them rather than what I covered in this article like concurrency issue, cancellation of tasks, exception handling, and task coordination…

Again, we have concluded that these two approaches enable us to finish our task faster (utilizing the wait-time and free-resource). But they are subtly different.

References

--

--

Thanh Le
Geek Culture

A Software Technical Architect — Who code for food and write for fun :)