Monday, September 11, 2017

Basic overview of Tasks and Task-based Programming in .NET 4+

What and Why

Async Programming - hide latency of potentially long-running or blocking operations (i.e. I/O) by starting them in the background.

Parallel Programming - reduce time of CPU-bound computations by dividing workload & executing simultaneously.

Here we are talking about Tasks and Task Parallel Library (TPL) which gives us:
  • Cancelling
  • Easier Exception Handling
  • Higher-level constructs
  • ...

NOTE: Before this we had
  • Threads
  • Async Programming Model (i.e. async delegate invocation)
  • Event-based Async Pattern (i.e. BrackgroundWorker class)
  • QueueUserWorkItem

Async / Parallel Components of .NET 4

Task Parallel Library (TPL) - library of functions for tasks and the notion of a task
Task Scheduler - responsible for mapping tasks to available worker threads
Resource Manager - manages pool of worker threads
Parallel LINQ (PLINQ) - like link, but runs in Parallel
Concurrent Data Structures - queue, bag, dictionary

Use Cases

Interactive UI
In the UI if there is a non-async chunk of code executing in the UI thread then while the computation is running the UI will be unresponsive. For example you can't drag the window properly or click any other buttons, etc.

Processing requests in parallel such as a website or doing independent computations.

Creating a task

using System.Threading.Tasks;
Task t = new Task( code );
t.Start();

code = computation to perform
Start() = tells .NET that task *can* start, then returns immediately. The program "forks" and now there are two code streams that are executing concurrently (original and T).

Task

A task = object representing an ongoing computation
Provides:

  • Check status
  • wait
  • harvest results
  • store exceptions
Think of a task as an object having the following properties
  • Status
  • Result
  • Exception

Types of Tasks

Code Tasks

Executes given operation.
Example: 
Task t1 = Task.Factory.StartNew(() => { /* code */});

Facade Tasks

Task over existing operation. You use a facade task to provide a common API to the different task technologies that have been used over the years. So, instead of rewriting existing async code you can still benefit from the higher level constructs of a Task based api.
Example: 
var op = new TaskCompletionSource<T>();
Task t2 = op.Task;

Execution model

  • Code-based tasks are executed by a thread on some processor
  • Thread is dedicated to task until task completes.
  • If there is only one core then the threads share the core. This has extra overhead, but allows UI and tasks to be running concurrently. UI isn't intensive so not a problem sharing on thread and switching between the two of them. 
  • Ideally there are multiple cores and each task runs on a different core such that they are running concurrently and in parallel with less overhead.

Actions

When you create a new Task object one of the signatures accepts an Action as the parameter. An easy way to pass your chunk of code as an Action is with a Lambda expression as shown below.

Task t = new Task( () => { many lines of code here} );
t.Start();

Equivalent, but slightly more efficient way of creating the task and starting it in one line:

Task t = Task.Factory.StartNew( () => { many lines of code here } );

The multiple lines of of code will now execute in a background thread.

Reference

Content is based on Pluralsight video called Introduction to Async and Parallel Programming in .NET 4 by Dr. Joe Hummel. 

No comments: