Wednesday, 19 February 2014

More about Tasks in C#


In the last article, we looked at what Task is, how it can be instantiated and why it was added to the .net framework. In this article, we will take a deeper look into the Task class and see more about its inner workings and usage scenarios.

Task as you already  know is used for running some piece of work asynchronously. But how does it execute this work? To answer this question, I will take you a bit back to the Threading.ThreadPool clas. As you know the ThreadPool object was used in running queued user work items. This pool  is used to execute action delegates whose execution can be deferred without causing any problem to the application state. This pool is controlled by the common language runtime (CLR) which modifies the number of threads in the pool depending on the current load of the application and the number of work items currently queued for the pool. 

The Task object executes its work item on a highly modified and adapted pool similar to the ThreadPool (infact in an enhanced version of the good old ThreadPool). This enhanced pool scales well both in performance and under heavy load so an assumed limitless amount of work can be run in it.

Tasks methods and properties

The task class has many instance and static methods and properties with several variations and overloads. Infact trying to cover all of them, one will be doing a very poor job of re-documenting what has already been documented in the great Msdn documentation, but be that as it may we will still take a look at some of the more common properties and methods that can be used in an everyday scenario. Some of these properties include: Exception, IsCompleted, IsCanceled, IsFaulted, Id and CurrentId. Also we will take a look at some of the instance and static methods like Run(), Start(). Lets get to it.

1.  Exception: This is a property that returns an AggregateException when an exception occurs in the executed task. But if no exception occurred, this property returns null. This exception property always return AggregateException (so far an exception occured in the executed task) no matter the type of exception that was thrown in the task. The AggregateException is used by the .net framework as a container to wrap all the exceptions that occured during the execution of the task. This means that you can still access  all the thrown exceptions by calling the Handle() method of the AggregateException class.

2. IsCompleted: This property is used to check if a task has completed its work. This is usually used for tasks that return a value and gets called before the Result property of the task is queried.

3. IsCanceled: A CancellationTokenSource object  can be passed to a task when it is being initialized. Now this token is used to cancel a task cooperatively. The IsCanceled property is used to check whether the task was canceled by the CancellationTokenSource object.

4. IsFaulted: This property is used to check whether an exception occurred in the task during its execution. It is usually used to query whether a fault (exception) occured before calling the Exception property to get the exceptions.

5. CurrentId: This static property is used to get the Id of the currently executing Task. One thing we have to know about CurrentId  is that it is usually lazily evaluated.

6. Id: This instance property returns the Id of the Task on which it is called. Like CurrentId it is lazily evaluated and only gets assigned whenever it is requested.

Having Explained the Start() and Run() static methods in the last post the next thing we are going to do is to properly examine it using an example.

static void Main(string[] args)
            //Initilization stage
            var cancellationToken = new CancellationTokenSource();
            var task1 = Task.Factory.StartNew(() => DoNothing(cancellationToken),            cancellationToken.Token);
            var task2 = Task.Run(() => JustSleep());

            //The main thread slept so that the tasks will have time to 
            //be schduled and started before the main thread runs to completion.
            //Check if task1 has completed             if(!task1.IsCompleted)             {                 cancellationToken.Cancel();             }                         if (task2.IsFaulted)             {                 var exceptions = task2.Exception;                 if (exceptions != null)                 {                     Console.WriteLine("Task exception: " + exceptions.GetType());                     var errors = exceptions.Flatten();                     errors.Handle(e =>                     {                         Console.WriteLine(e.Message);                         return true;                     });                 }             }             Console.ReadLine();         }         public static void DoNothing(CancellationTokenSource token)         {             while (!token.IsCancellationRequested)             {                 Console.WriteLine("Mehn! am just sleeping, but wake me up when Is CancelRequeseted calls");                 Thread.Sleep(100);             }         }         public static void JustSleep()         {             Thread.Sleep(10);             Console.WriteLine("Since am asked to do nothing I will just be 
            sleeping for the main time");
            throw new Exception("You sleep too much, therefore you are fired!");

This my Nonsense application  prints the following to the console screen:

NB: Some of the methods we touched upon are shown in bold font face in the code sample


In this post, we covered some of the methods and properties available in the Task class. In the next series of post we will continue from where we stopped showing the usage and implementation of some of the methods in the Task class and such classes like CancellationTokenSource, TaskCreationOption, TaskScheduler among others. Happy coding.....

No comments:

Post a Comment