Microsoft Exam 70–483: Programming in C# — Objective 1.1: Implement multithreading and asynchronous processing

Luis Felipe (LuisDev)
6 min readNov 12, 2017

--

Hello, folks!

Well, it took me additional weeks to release this story, since I was going through my Specialization’s exam week. Now that I’m done with it, I finally finished that third story.

As I said in the last Story, I am going to cover the topics related to the first objective of the required skill Manage Program Flow.

Since there is a lot of content to cover, I am going to try to be the more succinct as possible! But, of course, I am going to show you code. :)

All the code for the certification series can be found here.

Understanding threads

Could you imagine if the CPU could only execute one operation at a time? How could it appear responsive to the user while processing tasks? Yeah, It would not. For example, while the Command-Line is processing an input command, you cannot enter any other command.

To address such problems, the thread concept was created. By using threads, the Operation System can keep Applications running in its own process (which in turn runs in its own thread), making sure that if that application crashes, it will not affect other processes. That way , the operation system appears responsive to the user actions.

In Windows, each thread is executed for a certain amount of time. At end of this time period, the OS switches to another thread, saving its current context.

It does not come without a cost, however. Threads consume memory, since Windows has to keep the context of the thread saved, in order to be able to restore it on each switch.

You, as a programmer, have to evaluate the needs of your current task, and decide whether implementing multithreading is the solution or not.

If you decide to implement multithreading, you can be sure that C# and .NET Framework has a whole world of possibilities to support you.

Using the Thread class

First of all, you can find the Thread class in the System.Threading namespace. It allows you to create new threads, manage their priority, and monitor their status.

Let me show you an example of how to create a Thread instance and execute some code on this Thread.

Example:

As you see, in this code example, we can create a Thread instance by passing a delegate (which is passed implicitly here). While the Thread is running, we can run the for loop and print some stuff on our screen, from the Main method.

The Thread.Sleep(0) is used to signal Windows that this thread is finished, which then switches to another thread immediately. If we do not use it, it would have to wait for the entire time-slice of the thread before switching.

The Join method is called to make the main Thread wait until all other threads finishes its execution.

A Thread can be foreground or background. The foreground threads keep the application alive. Before all foreground threads are ended, the Common Language Runtime (CLR) can not finish your application. So, if you start a background Thread, and do not start any foreground Thread in your code, the application will immediately shut down as you run it.

Example:

As you execute it, you will notice that… Well, your methods seems to be not being executed! That is the expected behavior, as I told you before.

There is another overload for the Thread constructor. You can pass an instance of ParameterizedThreadStart delegate. After doing that, you can pass a parameter to the Start method. Remember that the Start method asks an object parameter, so you will have to cast the value inside the method.

Thread pools

Creating a Thread, by using the Thread class, can be time and resources costly. You can reuse threads by using a Thread Pool, so instead of “killing” your thread, you just send it back to the Thread pool, where it will be available to be reused whenever a new request comes in.

Example:

The shortcoming of using the Thread Pool to queue a work item is that there is no way (a built-in way, at least) to know whenever an operation finishes or what is its return value.

Using Tasks

A Task represents an asynchronous operation and it is in the namespace System.Threadig.Tasks.

It does allow you to know when the work is completed and the result value, in case there is one.

Example:

The method Wait() is the equivalent of Join() method, called on a Thread.

When you try to access the Result property of the Task<string>, the thread will be forced to wait until you get the result (so that is why there is no need to call Wait() on this Task).

You can execute some work right after the execution of a Task, being able to access the return value of the Task, with the ContinueWith method.

Example:

In addition to calling Wait on a single Task, you can use instead the WaitAll to wait for all current executing Tasks.

Example:

If you execute that piece of code, you will notice that it did not take 3,0s, but around 1,0s! That proves you that these Tasks run in a parallel way!

Using the Parallel class

There is another class, in the System.Threading.Tasks namespace, that can be used for processing work in a parallel way. It is the Parallel class, and it has some static methods that you can use in order to parallelize the work.

It is important to understand when to use parallel processing though. It can increase performance when working with big sets of work, but can decrease performance if you are executing smaller sets of works.

Example:

As you can notice by executing that code, the order of the output numbers is not guaranteed, since it is executed parallelly.

Using async and await

You can use asynchronous code in order to improve scalability when executing I/O operations (data retrieval, for example), since Windows blocks your thread until an I/O operation finishes.

C# 5.0 brought two new keywords that simplify the task of writing asynchronous code: async and await.

The async keyword is used to “annotate” a method for asynchronous operations. It is required if you want to use the await keyword inside a method.

The use of the await keyword allows the UI to be responsive and allows the current thread to do some other work.

Example:

In this example, a call to the JSONPlaceholder API (Fake Online REST API for Testing and Prototyping) is performed, and a JSON object response is printed as a string.

Using Parallel Language Integrated Query (PLINQ)

You probably have heard of the Language-Integrated Query (LINQ) feature of C#. It allows you to perform a whole variety of queries over all kinds of data. LINQ methods are basically extension methods implemented for a lot of collections-like structures.

The Parallel Language-Integrated Query (PLINQ) can be understood as the “parallel” version of the LINQ, offering some addition operators for parallel operations, in addition to the standard query operators of LINQ.

It is not certain that the query will run in a parallel way. The runtime will be the one deciding whether it makes sense to make the query into a parallel one or not.

You have to use the AsParallel() method to submit the query to be processed in a parallel way. As I said before, it is not guaranteed that the runtime will execute it using the parallel approach.

Example:

There is one important thing to keep in mind: Parallel processing does not guarantee any particular order. However, you can guarantee that by using the AsOrdered() method.

Example:

If you want to iterate over a collection in a parallel way, you can use the ForAll method, passing a Lambda expression:

Example:

Using concurrent collections

The .NET Framework offers some classes that are both thread-safe (it means that they are “prepared” to be operated by multiple threads at the same time) and scalable. If you need a collection which you are going to need to remove and add items using multiple threads concurrently, they are what you are looking for.

They can be found in the System.Collections.Concurrent and here are some of them:

  • BlockingCollection<T>
  • ConcurrentBag<T>
  • ConcurrentDictionary<TKey,T>
  • ConcurrentQueue<T>
  • ConcurrentStack<T>

Well, that is basically what the book Exam Ref 70–483: Programming in C# covers for the first objective of the required skill Manage program flow.

I hope you like it! I will soon create the Anki deck and add the cards regarding this first Story, and publish them on the GitHub!

The C# code is already on GitHub.

See you in the next Story!

PS: If you find this Story useful, I invite you to hit the Clap button. Also, I would be happy to see you as my new follower!

--

--

Luis Felipe (LuisDev)

3x Microsoft MVP, 9x Microsoft Certified, .NET and Azure Consultant