Open In App

Task Parallel Library in C#

Last Updated : 04 Mar, 2025
Comments
Improve
Suggest changes
Like Article
Like
Report

In C#, Task Parallel Library (TPL) is a collection of APIs that provides more control over parallel and asynchronous programming. It is present in the System.Threading.Tasks namespace. TPL simplifies multithreading by managing thread scheduling and execution efficiently. It includes features like data parallelism, task scheduling, and supportive cancellation. TPL provides a higher level of abstraction over traditional threading mechanisms, making it easier to write, read, and maintain parallel code.

  • TPL reduces CPU overheating in long-running tasks.
  • It makes Thread Management easier and doesn't worry about race conditions.
  • Provide an efficient way to cancel a task co-operatively through CancellationToken.
  • TPL is used for its better CPU utilization, enhancing performance, and is a better alternative to the traditional Thread class.

Example: This program creates Tasks to print numbers using the Task Parallel Library.

C#
// Implementation of Multithreading 
// in C# using Task Class
using System;
using System.Threading.Tasks;

class Geeks
{
    static void Main()
    {
        // Creating and starting two 
        // tasks to run concurrently
        Task task1 = Task.Run(() => PrintNumbers());
        Task task2 = Task.Run(() => PrintNumbers());

        // Waiting for both tasks to complete 
        // before exiting the program
        Task.WhenAll(task1, task2).Wait();
    }

    static void PrintNumbers()
    {
        // Printing numbers from 0 to 2 in each task
        for (int i = 0; i < 3; i++)
        {
            Console.WriteLine(i);
        }
    }
}

Output
0
1
2
0
1
2

Task Class

Task class used for more complex or parallel tasks is an important part of the TPL. The Task class allows us to create tasks that can run asynchronously, improving both performance and code readability. It also simplifies task management by handling thread pooling and synchronization. It reduces the task of managing threads manually like the traditional way using Thread Class.

Example: This program demonstrates how to create and run a task using the Task class.

C#
// Implementation of Multithreading in C# using 
// Task Class to run multiple tasks concurrently
using System;
using System.Threading.Tasks;

class Geeks
{
    static void Main()
    {
        // Running two tasks concurrently
        Task task1 = Task.Run(() => PrintNumbers());
        Task task2 = Task.Run(() => ReverseNumbers());

        // Wait for both tasks to complete
        Task.WhenAll(task1, task2).Wait();
    }

    static void PrintNumbers()
    {
        // Printing numbers from 0 to 2
        for (int i = 0; i < 3; i++)
        {
            Console.WriteLine(i);
        }
    }
    
    static void ReverseNumbers()
    {
        // Printing numbers from 2 to 0
        for (int i = 2; i >= 0; i--)
        {
            Console.WriteLine(i);
        }
    }
}

Output
2
1
0
0
1
2

Explanation: In the above example we create two tasks one is for printing the numbers from the start and one for printing the numbers in the reverse order and using the method Task.Run() to start the task and pass the method in the argument using the anonymous function.

Task Cancellation

Task cancellation is an important part of the Task class. Task class provides an effective way to cancel a task co-operative using the CancellationToken from the CancellationTokenSource class. Thread.Abort() is not recommended in modern C# as it is unsafe. Instead, Task cancellation is handled properly using CancellationToken without forcibly terminating a thread which will throw an exception which will interrupt the flow of the program.

Example: This program is used to demonstrate how to cancel a task using the CancellationToken.

C#
// Implementation of Multithreading in C# using 
// Parallel.ForEach with CancellationToken support
using System;
using System.Threading;
using System.Threading.Tasks;
using System.Collections.Generic;

class Geeks
{
    static void Main()
    {
        List<string> data = new List<string> { "Hello", "Geek", "How", "Are", "You" };

        // Create a CancellationTokenSource
        CancellationTokenSource cts = new CancellationTokenSource();
        CancellationToken token = cts.Token;

        // Create a task that uses the CancellationToken
        Task.Run(() =>
        {
            try
            {
                Console.WriteLine("Task started.");
                Parallel.ForEach(data, new ParallelOptions { CancellationToken = token }, item =>
                {
                    // Check for cancellation
                    token.ThrowIfCancellationRequested();
                    Console.WriteLine($"Data [{item}] on thread: {Thread.CurrentThread.ManagedThreadId}");
                    
                });
                Console.WriteLine("Task completed.");
            }
            catch (OperationCanceledException)
            {
                Console.WriteLine("Task was canceled.");
            }
        }, token);

        
        Task.Delay(200).Wait();

        // Cancel the task
        cts.Cancel();

        // Dispose the CancellationTokenSource
        cts.Dispose();

        Console.WriteLine("Main method completed");
    }
}

Output
Task started.
Data [How] on thread: 7
Data [Hello] on thread: 4
Data [Are] on thread: 8
Data [Geek] on thread: 6
Data [You] on thread: 7
Task completed.
Main method completed

Parallel Class

Parallel class is a part of TPL. and present in the System.Threading.Tasks namespace. It provides various looping methods to achieve parallelism and It is an easier way to iterate through the parallel programs. It has powerful abstract methods which are used to perform different operations on parallel tasks. These are the important iterating methods that are mainly used in the parallel class.

Parallel.For()

This method is used to execute the loops in parallel. It is an efficient way to iterate through the tasks which work independently. And we can use this loop to iterate concurrently which enhances the computational speed and reduce the overhead. It divides the loop iterations into separate threads and runs concurrently.

Syntax:

Parallel.For(int fromInclusive, int toExclusive, Action<int> body)

  • fromInclusive: The starting index of the loop.
  • toExclusive: The exclusive end index of the loop (one past the last index).
  • body: We need to pass the Action<int> delegate which is the action to be performed.

It takes three parameters: the start index, the end index, and a delegate that represents the body of the loop.

Example:

C#
// Implementation of Multithreading in C# 
// using Parallel.ForEach for concurrent processing
using System;
using System.Threading.Tasks;
using System.Collections.Generic;

class Geeks
{
    static void Main()
    {
        List<string> data = new List<string> { "Hello", "Geek", "How", "Are", "You" };

        // Process each element in parallel
        Parallel.ForEach(data, ele =>
        {
            Console.WriteLine($"Data => {ele} on thread:- {System.Threading.Thread.CurrentThread.ManagedThreadId}");
        });
    }
}

Output
Data => Hello on thread:- 1
Data => Geek on thread:- 4
Data => How on thread:- 5
Data => Are on thread:- 6
Data => You on thread:- 7

Explanation: The Parallel.ForEach() loop separates the data present in the list in small chunks and runs it on a different thread as shown in the output note that if we run this code it will give the output differently each time.

Parallel.ForEach()

This method is also used to iterate parallel loops and the using this loop we can easily iterate over the collections in C# like list, array set or any other collection. The main difference is that it works directly on the collection and we don't need to specify the start and end index instead we just simply pass the collection in the argument directly.

Syntax:

ForEach<TSource>(IEnumerable<TSource> source, Action<TSource> body)
  • collection: We can pass any collection like array, list or set which we want to iterate.
  • body: An Action<T> delegate that defines the work to be done for each item in the collection.

Example:

C#
// Implementation of Multithreading in C# using 
// Parallel.ForEach for concurrent data processing
using System;
using System.Threading.Tasks;
using System.Collections.Generic;

class Geeks
{
    static void Main()
    {
        List<string> data = new List<string> { "Hello", "Geek", "How", "Are", "You" };

        // Process each element in parallel
        Parallel.ForEach(data, ele =>
        {
            Console.WriteLine($"Data => [{ele}] on thread:- {System.Threading.Thread.CurrentThread.ManagedThreadId}");
        });
    }
}

Output
Data => [Hello] on thread:- 1
Data => [Geek] on thread:- 1
Data => [How] on thread:- 1
Data => [Are] on thread:- 1
Data => [You] on thread:- 1

Explanation: In the above code we use the Parallel.ForEach() method executes the data collection on the separate thread as shown in the output and note that each time we run the program the output will be different each time.

Note: the thread assignments may change each time.

Parallel.Invoke()

This method is used to run multiple methods or any action concurrently. And doesn't require to creation of a loop. We can use this method when we have multiple actions to perform and they are not dependent on each other and can be executed parallelly. It runs the actions at the same time which enhances the performance.

Syntax:

Parallel.Invoke(Action[] actions);

actions: An array of Action delegates representing the independent tasks that need to be executed in parallel.

Example:

C#
// Implementation of Multithreading in C# using 
// Parallel.Invoke for concurrent execution of multiple actions
using System;
using System.Threading.Tasks;
using System.Collections.Generic;

class Geeks
{
    static void Main()
    {
        List<string> data = new List<string> { "Hello", "Geek", "How", "Are", "You" };

        // Execute multiple tasks in parallel
        Parallel.Invoke(
            () => Console.WriteLine($"Data => [{data[0]}] on thread:- {System.Threading.Thread.CurrentThread.ManagedThreadId}"),
            () => Console.WriteLine($"Data => [{data[1]}] on thread:- {System.Threading.Thread.CurrentThread.ManagedThreadId}"),
            () => Console.WriteLine($"Data => [{data[2]}] on thread:- {System.Threading.Thread.CurrentThread.ManagedThreadId}"),
            () => Console.WriteLine($"Data => [{data[3]}] on thread:- {System.Threading.Thread.CurrentThread.ManagedThreadId}"),
            () => Console.WriteLine($"Data => [{data[4]}] on thread:- {System.Threading.Thread.CurrentThread.ManagedThreadId}")
        );
    }
}

Output
Data => [Hello] on thread:- 1
Data => [Geek] on thread:- 6
Data => [How] on thread:- 5
Data => [Are] on thread:- 5
Data => [You] on thread:- 4

Explanation: In the above code we use the Invoke() method from the parallel class and we don't need to create a loop we just write the method in the lambda function and it executes parallelly.

Error Handling

TPL provides a better way to handle the error using AggregateException. When we are using the task class and it not working according and throws an error we need to handle it so the flow of the program will execute accordingly. Using the AggregateException we can handle different kinds of exceptions.

Example: Handling an error in TPL using the AggregateException in Task class.

C#
// Implementation of Exception Handling in C# 
// using Task and AggregateException
using System;
using System.Threading;
using System.Threading.Tasks;
using System.Collections.Generic;

class Geeks
{
    static void Main()
    {
        List<string> data = new List<string> { "Hello", "Geek", "How", "Are", "You" };

        // Create a task that may throw exceptions
        Task task = Task.Run(() =>
        {
            
            if (data.Contains("How"))
            {
                throw new InvalidOperationException("Error: data list contains the word \"How\"");
            }
        });

        try
        {
            // Wait for the task to complete
            task.Wait();
        }
        catch (AggregateException ex)
        {
            // Handle the exception
            Console.WriteLine($"Handled exception: {ex.Message}");
        }
    }
}

Output
Handled exception: One or more errors occurred. (Error: data list contains the word "How")

Explanation: In the above example we create a custom error if the list contains a questionable word like "How" so it will throw an error and we handled this error using the AggeregateException.


Next Article

Similar Reads