Tuesday, 18 February 2014

A gentle introduction to tasks in C#

Introduction

In the last few articles, we have been looking at threads and some of the ways by which we can synchronize and maintain states in a multi-threaded application. In this post and in the next few articles, we are going to have a look at Tasks. What tasks are and why it exists when to use it and why is advisable to forget using old-timers like threads that accomplish the same objective in current applications.

So what is a Task

Threads were truly a great way to run codes in parallel but it had numerous limitations like not being able to return a result after its execution and not being reusable (i.e : could not be used to execute multiple actions). So the Task Parallel Library was Microsoft answer to some of those limitations. Tasks was also birthed to take advantage of the multi-core machines that have entered into main stream use.

The Task Parallel Library was implemented to ease off the pain usually encountered by developers when writing asynchronous code. This library enables developers to write asynchronous code synchronously. The task class is an abstraction on top of the good old threads that was added to the .net framework version 4. It lives in the System.Threading.Tasks namespace. It is primarily used to run work in parallel and was written to take care of the limitations of threads. Unlike threads that run on the operating system threads managed by the CLR. Tasks objects runs in an enhanced ThreadPool. It has two flavors: the Task object that takes in an action delegate but does not return a value and a generic one that takes in a func delegate and returns a value. Its usage depends on the scenario of the code that you are writing. So you use the action delegate task object when you don't want to return a value a the and the func delegate when you want to return a value.

Creation and execution of the tasks object

A task can be started in one of these three methods:

Task.Run(/* Takes in an action delegate, an anonymous method or a lamda expression.*/);ORTask<int>.Run(/*Takes in a func delegate, an anonymous method or a lamda expression that returns a value */);

You use this style when you do not need much control but just want the task to be started and run.

Another way in which a task can be executed is by calling the StartNew() method in the TaskFactory class, added as a static property to the Task class for convenience as shown below.

Task<string>.Factory.StartNew(/*Takes in a func delegate, an anonymous method or a lamda expression that returns a value */);ORTask.Factory.StartNew(/* Takes in an action delegate, an anonymous method or a lamda expression.*/);
This style is typically used when you want more control of the task creation process but want the task to be automatically started once it had been created.

Yet another method of creating a Task object is by instantiating the constructor and passing the asynchronous code as argument as shown below.var task = new Task(/* Takes in an action delegate, an anonymous method or a lamda expression.*/);task.Start();
You can use this style when you want the most control on how a task is created, started and when you to pass a customized arguments like a custom TaskScheduler object.

NB: I must mention that there are overloads that receive more parameters like TaskCancelationToken
for cooperative cancelling of the task, TaskCreationOptions to specify how you want the task to behave once it has been created.

Conclusion

In this post we briefly looked what a task is, when it can be used, how it is created. In this next few post, we will look at the other aspects of the Task class like exception handling, continuations, using the async/await keyword added in C# version 5 and a demo program in Asp.net Mvc to show its usage.