Working with Tasks in .NET Framework 4.0 – An Insight

CodeGuru content and product recommendations are editorially independent. We may make money when you click on links to our partners. Learn More.

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!

More by Author

Get the Free Newsletter!

Subscribe to Developer Insider for top news, trends & analysis

Must Read