Working with Tasks in .NET Framework 4.0 - An Insight

The .Net framework 4.0 version is mounted with lots of new features, out of which the Parallel Programming using the Task Parallel Library (TPL) is the one gaining more interest. In this article I will write about the Task class under the namespace System.Threading.Tasks. The asynchronous operations scheduled through a Task class make sure that the system resources are used intelligently, such as scheduling the threads on different cores available on the processor; it also provides a lot of control over the scheduled tasks, like fetching the return value, cancelling a task, building nested tasks, etc.

Starting a Parallel Task

In this section let us see the various ways of starting the tasks. A Task class can be directly instantiated and started like that of a Thread class. Below is the sample code.

    static void Main(string[] args)
    {
        Task task1 = new Task(PrintA5Times);
        task1.Start();
        Console.ReadLine();
    }

In the above code a task is created when Task class is instantiated and only gets scheduled when the Start method is called. A task scheduler takes care of loading these tasks onto different threads. There is a factory class available, which can be used to create the task and schedule it instantly.

    static void Main(string[] args)
    {
        Task.Factory.StartNew(() =>
            {
                Console.WriteLine("This task is created and scheduled through task factory!");
            });
 
        Console.ReadLine();
    }

Synchronizing Tasks

When dealing with asynchronous operations, the most important thing is to know about the synchronization techniques. Below are the different ways to synchronize tasks.

Wait

Wait is an instance method available in the Task object. Calling this method only blocks the actual task until the task is completed or until a specified timeout.

    static void Main(string[] args)
    {
        Task task1 = new Task(PrintA1000Times);
        task1.Start();
        Task task2 = new Task(PrintB500Times);
        task2.Start();
 
        //Perform other operations
            
        //Wait for task1 to complete
        task1.Wait();
        Console.WriteLine("Task1 completed!");
 
        //Perform some more operations
 
        //Wait for task2 to complete with a time out
        task2.Wait(10000);
        if(task2.IsCompleted)
            Console.WriteLine("Task2 completed!");
        else
            Console.WriteLine("Task2 Timed out!");
 
        Console.ReadLine();
    }

WaitAll

WaitAll is a static method in the Task class and it can be used to synchronize all of the tasks. This method call will make the execution wait until all of the scheduled tasks are completed.

    static void Main(string[] args)
    {
        Task task1 = new Task(PrintA1000Times);
        task1.Start();
        Task task2 = new Task(PrintB500Times);
        task2.Start();
        Task task3 = new Task(SleepFor1Min);
        task3.Start();
 
        Task.WaitAll(task1, task2, task3);
        Console.WriteLine("All the tasks completed!");
 
        Console.ReadLine();
    }

WaitAny

This is again a static method in the Task class and it makes the execution wait until any one of the task is completed.

    static void Main(string[] args)
    {
        Task task1 = new Task(SleepFor3Min);
        task1.Start();
        Task task2 = new Task(SleepFor2Min);
        task2.Start();
        Task task3 = new Task(SleepFor1Min);
        task3.Start();
 
        int taskIndex = Task.WaitAny(task1, task2, task3);
        Console.WriteLine("Task with index {0} is completed", taskIndex);
 
        Console.ReadLine();
    }

Dependent Tasks

You can also create dependent Tasks by either attaching the child task to a parent task or by specifying a task to be scheduled only when the dependent task operation is completed.

ContinueWith

The ContinueWith method creates a new task and schedules it once the dependent task is completed. Below is the example demonstrating it.

    static void Main(string[] args)
    {
        Task<int> task1 = new Task<int>(GetTotal);
        task1.Start();
        Task task2 = task1.ContinueWith(task => PrintTotal(task.Result));
 
        Console.ReadLine();
    }

AttachToParent

This attaches the child task to the parent so a Wait method call on the parent task will be released only if the child task is completed. Below is an example.

    static void Main(string[] args)
    {
        Task task1 = Task.Factory.StartNew(() =>
            {
                PrintA10Times();
                Task task2 = new Task(SleepFor1Min, TaskCreationOptions.AttachedToParent);
                task2.Start();
            });
 
        task1.Wait();
        Console.WriteLine("Both parent and child tasks are completed!");
 
        Console.ReadLine();
    }

Cancellation of Tasks

In this section let us look at an example where a task can be cancelled. The basic idea is to pass the token value from the CancellationTokenSource object to the Task while creating it and when the Cancel method of the TokenSource is called, the corresponding Task is cancelled.

    static void Main(string[] args)
    {
        CancellationTokenSource tokenSource = new CancellationTokenSource();
        Task task1 = new Task(SleepFor2Min, tokenSource.Token);
 
        //Wait for some time
        task1.Wait(1000);
        //Cancel the task
        tokenSource.Cancel();
 
        Console.WriteLine("Status of task1: {0}", task1.Status);
 
        
        Console.ReadLine();
    }

There many more things that you can accomplish using the Task class that the .net framework 4.0 has provided as a part of Task Parallel Library. With this insight on tasks I leave it to the user to explore more.

Happy reading!



Related Articles

Comments

  • There are no comments yet. Be the first to comment!

Leave a Comment
  • Your email address will not be published. All fields are required.

Top White Papers and Webcasts

  • Live Event Date: December 11, 2014 @ 1:00 p.m. ET / 10:00 a.m. PT Market pressures to move more quickly and develop innovative applications are forcing organizations to rethink how they develop and release applications. The combination of public clouds and physical back-end infrastructures are a means to get applications out faster. However, these hybrid solutions complicate DevOps adoption, with application delivery pipelines that span across complex hybrid cloud and non-cloud environments. Check out this …

  • On-demand Event Event Date: October 29, 2014 It's well understood how critical version control is for code. However, its importance to DevOps isn't always recognized. The 2014 DevOps Survey of Practice shows that one of the key predictors of DevOps success is putting all production environment artifacts into version control. In this webcast, Gene Kim discusses these survey findings and shares woeful tales of artifact management gone wrong! Gene also shares examples of how high-performing DevOps …

Most Popular Programming Stories

More for Developers

RSS Feeds