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!