A deep dive inside the Task Parallel Library in .Net Framework

Take advantage of the Task Parallel Library in .Net Framework to build applications that can leverage multiple cores in your system and increase the application's responsiveness and throughput

Task Parallel Library

Microsoft has provided support for parallel programming in .Net Framework to leverage the benefits of multi core systems. In this post, I will present a discussion on the support for Parallel Programming in .Net Framework -- the basic concepts and an overview of what’s new in .Net Framework 4.6.

Concurrency and parallelism: how do they differ?

First off, what is a thread, and what is multi-threading? Well, a thread is the path of execution within a process. It is the smallest individually schedulable entity within a process. An application can be single threaded or multi-threaded. A single threaded application can execute one thread at a time, and hence they are incapable of leveraging today’s multi-core systems. Multi-threaded applications are different. They can have multiple threads in memory and can execute those threads simultaneously if you have multiple cores in your system. Ah...wait. Let not get confused. Read through the sections that follow -- your queries will all be answered in no time.

A single core can execute only one thread at a time. However, you can still have multi-threading in a single core. In systems having one core, you would have only one thread in the execution state at any given point of time but there would be many other threads residing in memory -- waiting for their turn of the processor. Such threads are in the "ready" state and are stored in a queue waiting for their turn to be allocated the processor. So, if you have n number of threads in your system with one of them allocated the processor and in the running state, you would have n - 1 threads residing in the ready or runnable state waiting for their turn of being allocated the processor.

Asynchrony and parallel or concurrent execution are concepts often confusing to many developers. Concurrent execution refers to the ability of executing tasks concurrently -- you can have tasks being executed concurrently in systems having a single core. Parallelism on the contrary needs multi-core systems, i.e., you can have parallel execution of tasks only on systems that have multiple cores.

Why should we be worrying about implementing asynchrony in our applications? Let me explain this with an example. Consider two tasks, T1 and T2, that have to be executed by an application. Concurrent executions of these tasks would imply that either one of the tasks are in execution state or one of them would be executed by the processor alternatively. So, one of these tasks can be completed ahead of the other. Task parallelism on these tasks T1 and T2 can only be achieved if both these tasks are executed in parallel, i.e., simultaneously. This is only possible if you have multiple cores with each of the tasks being executed on one core at a given point of time. Asynchrony if implemented properly would boost the overall throughput of the application and increase the application’s responsiveness.

Parallel Programming in .Net

Support for parallel programming was first introduced in .Net Framework 4. You can execute your LINQ queries in parallel using Parallel LINQ (PLINQ). PLINQ is a part of the Parallel Extensions Library (earlier known as Parallel Framework Extensions or PFX). The Task Parallel Library is a set of types in the System.Threading and System.Threading.Tasks namespaces. The Task Parallel Library (TPL) has been updated in .Net Framework 4.6. A task resembles an asynchronous operation. Task parallelism is the ability to execute tasks in parallel, and it enables you to use your system resources more efficiently with a better programmatic control. You need to make use of the System.Threading.Tasks.Task or System.Threading.Tasks.Task<TResult> class to create tasks (a schedulable unit of work). While the former is used to create tasks that don't return a value, the latter is used to create tasks that have return values.

What’s new in the Task Parallel Library in .Net Framework 4.6?

The Task class in .Net Framework 4.5 didn't expose methods for creating canceled or faulted tasks - you have had to create them manually using TaskCompletionSource. With .Net Framework 4.6 you have two new static helpers called Task.FromCanceled and Task.FromException. Microsoft .Net Framework 4.6 includes a new value called RunContinuationsAsynchronously to the TaskCreationOptions and TaskContinuationOptions enumerations. This helps to overcome situations where your code might go for a stack dive and potentially blow out the stack. A stack dive would occur when the same thread goes deeper and deeper into the stack. With the introduction of RunContinuationsAsynchronously in .Net Framework 4.6, the continuations would run asynchronously - the thread that releases a semaphore would move on and the continuation would be scheduled for another thread. RunContinuationsAsynchronously is particularly useful in pause-able asynchronous processing - you might have one asynchronous task pause and/or resume the execution of another.RunContinuationsAsynchronously also makes sure that continuations that are scheduled to be resumed on a particular synchronization context always execute on that context asynchronously.
Suggested readings