• Home

Asynchronous Multi Threaded and Parallel Programming in .NET

January 16, 2026

Syndication Cloud

Asynchronous Multi Threaded and Parallel Programming in .NET
Asynchronous, Multi Threaded and Parallel Programming in. NETPhoto from Unsplash

Originally Posted On: https://medium.com/@darshana-edirisinghe/asynchronous-multi-threaded-and-parallel-programming-in-net-25d46eb2d28e

This article will cover the fundamentals of asynchronous programming in .NET, including the use of async and await keywords, the role of tasks, and how they interact with the thread pool. We will explore practical examples demonstrating asynchronous methods, handling exceptions in asynchronous code, and improving performance with parallel programming. Additionally, we’ll discuss best practices for writing efficient asynchronous code and common pitfalls to avoid. By the end of this article, you’ll have a solid understanding of how to implement asynchronous programming in your .NET applications.

Asynchronous Programming

Asynchronous programming in .NET allows a program to perform tasks without blocking the main thread, enabling the program to remain responsive. This is particularly useful for tasks that might take some time, such as file I/O operations, network requests, or any other long-running processes.

Key Concepts of Asynchronous Programming in .NET:

  1. Async and Await: These are keywords used to define asynchronous methods.
  • async: Used to declare a method as asynchronous.
  • await: Used to pause the execution of an async method until the awaited task completes.

2. Tasks: Represents an asynchronous operation. The Task class is used to handle and control these operations.

Relation to the Thread Pool

Thread Pool

A collection of threads managed by .NET to perform background tasks. Instead of creating a new thread every time an asynchronous task is performed, .NET uses threads from this pool to optimize performance.

When you use async and await, the method doesn’t block the main thread. Instead, it runs on a thread from the thread pool. Once the awaited task completes, it returns to the context it was called from, which is often the main thread.

Execution

public async Task<int> FetchDataCountAsync()
{
    // 1. Call a synchronous method to get all products. 
    // This runs on the current thread.
    var products = productService.GetAll();

    // 2. Calculate the length of the products array. 
    // This also runs on the current thread.
    var productLength = products.length();

    // 3. Call an asynchronous method to get all categories. 
    // This does not block the current thread.
    // The control returns to the caller until this task is completed.
    var categories = await catService.GetAll();
    // The method pauses here until catService.GetAll() completes.
    // Once completed, the result is assigned to the 'categories' variable.

    // 4. Calculate the length of the categories array. 
    // This runs on the current thread after the await completes.
    var catLength = categories.Length();

    // 5. Call an asynchronous method to get all limits. 
    // This does not block the current thread.
    // The control returns to the caller until this task is completed.
    var limits = await limitService.getAll();
    // The method pauses here until limitService.getAll() completes.
    // Once completed, the result is assigned to the 'limits' variable.

    // 6. Calculate the length of the limits array. 
    // This runs on the current thread after the await completes.
    var limitLength = limits.length();

    // 7. This runs on the current thread.
    return productLength + catLength + limitLength;
}

Explanation

Press enter or click to view image in full size

Real-World Application: PDF Generation

Let’s see how these async concepts apply to a common real-world scenario — generating PDF documents. PDF generation is an I/O-intensive operation that benefits greatly from asynchronous programming. IronPDF, a C# PDF library, provides async methods for HTML to PDF conversion, demonstrating exactly why we use async/await:

public async Task<byte[]> GenerateInvoicePdfAsync(string invoiceHtml)
{
    // Create renderer - this runs synchronously
    var renderer = new ChromePdfRenderer();
    
    // Convert HTML to PDF asynchronously
    // This doesn't block the thread while the Chrome engine renders
    var pdf = await renderer.RenderHtmlAsPdfAsync(invoiceHtml);
    
    // The method pauses here until rendering completes
    // Once done, we can process the result
    return pdf.BinaryData;
}

Notice how this follows the same pattern as our earlier example. The PDF rendering happens on a thread from the thread pool, allowing the main thread to remain responsive. This is particularly important when generating multiple PDFs — which we’ll explore further when we discuss parallel programming with Task.WhenAll and Parallel.ForEach later in this article.

This HTML to PDF converter demonstrates why async programming matters: without it, your application would freeze while generating each PDF, creating a poor user experience. Explore IronPDF 30-day trial here.

Asynchronous vs Synchronous

In .NET, synchronous methods typically do not directly interact with the thread pool unless they explicitly use it, such as via Task.Run or ThreadPool.QueueUserWorkItem. By default, synchronous methods run on the current thread that calls them. However, you can use the thread pool to run synchronous methods in a background thread, thus freeing up the main thread.

Execution

public int FetchDataSync()
{
    // 1. Call to get all products. 
    // This runs on the current thread and blocks until it completes.
    var products = productService.GetAll();

    // 2. Calculate the length of the products array. 
    // This runs on the current thread after the previous line completes.
    var productLength = products.length();

    // 3. Call to get all categories. 
    // This runs on the current thread and blocks until it completes.
    var categories = catService.GetAll();

    // 4. Calculate the length of the categories array. 
    // This runs on the current thread after the previous line completes.
    var catLength = categories.Length();

    // 5. Call to get all limits. 
    // This runs on the current thread and blocks until it completes.
    var limits = limitService.getAll();

    // 6. Calculate the length of the limits array. 
    // This runs on the current thread after the previous line completes.
    var limitLength = limits.length();

    // 7. This runs on the current thread after the previous lines complete.
    return productLength + catLength + limitLength;
}

Explanation

Multi-Threaded Programming

Multi-threaded programming in C# involves creating and managing multiple threads within a single application. This allows the application to perform multiple tasks concurrently, improving performance and responsiveness, especially on multi-core processors.

Key Concepts

  1. Thread: The smallest unit of a process that can be scheduled for execution. In C#, you can create and manage threads using the Thread class.
  2. Thread Pool: A pool of worker threads managed by the .NET Framework. It handles thread creation and management, which helps improve performance and resource management.
  3. Task: A higher-level abstraction over threads. Tasks are part of the Task Parallel Library (TPL) and provide a more efficient and easier way to work with asynchronous operations.

Scenario 1: No dependencies between thread results

using System;
using System.Threading;

class Program
{
    static void Main()
    {
        // Create three threads
        Thread thread1 = new Thread(new ThreadStart(Activity1));
        Thread thread2 = new Thread(new ThreadStart(Activity2));
        Thread thread3 = new Thread(new ThreadStart(Activity3));

        // Start the threads
        thread1.Start();
        thread2.Start();
        thread3.Start();

        // Wait for threads to complete
        thread1.Join();
        thread2.Join();
        thread3.Join();

        Console.WriteLine("All activities completed.");
    }

    static void Activity1()
    {
    }

    static void Activity2()
    {
    }

    static void Activity3()
    {
    }
}

Scenario 2: Dependencies between thread results

The output of Thread 1 is required to start Thread 2. Use a simple synchronization mechanism using ManualResetEvent to signal between threads.

using System;
using System.Threading;

class Program
{
    static ManualResetEvent activity1Completed = new ManualResetEvent(false);
    static string sharedData;

    static void Main()
    {
        // Create three threads
        Thread thread1 = new Thread(new ThreadStart(Activity1));
        Thread thread2 = new Thread(new ThreadStart(Activity2));
        Thread thread3 = new Thread(new ThreadStart(Activity3));

        // Start the threads
        thread1.Start();
        thread2.Start();
        thread3.Start();

        // Wait for threads to complete
        thread1.Join();
        thread2.Join();
        thread3.Join();

        Console.WriteLine("All activities completed.");
    }

    static void Activity1()
    {
        // Set shared data and signal completion
        sharedData = "Data from Activity 1";
        activity1Completed.Set();
    }

    static void Activity2()
    {
        // Wait for Activity 1 to complete
        activity1Completed.WaitOne();

        // Implementation
        var Act1Results = sharedData;     
    }

    static void Activity3()
    {
    }
}

Scenario 3: Handle Exceptions

using System;
using System.Threading;
using System.Threading.Tasks;

class Program
{
    static CancellationTokenSource cts = new CancellationTokenSource();

    static void Main()
    {
        // Create and start tasks
        Task task1 = Task.Run(() => Activity1(cts.Token), cts.Token);
        Task task2 = Task.Run(() => Activity2(cts.Token), cts.Token);
        Task task3 = Task.Run(() => Activity3(cts.Token), cts.Token);

        try
        {
            // Wait for all tasks to complete
            Task.WaitAll(task1, task2, task3);
        }
        catch (AggregateException ex)
        {
            // Handle the exception
            foreach (var innerEx in ex.InnerExceptions)
            {
                Console.WriteLine($"Exception: {innerEx.Message}");
            }

            // Revert changes here
            RevertChanges();

            // Signal that the tasks were cancelled
            Console.WriteLine("All activities stopped and changes reverted.");
        }
    }

    static void Activity1(CancellationToken token)
    {
        try
        {
            throw new Exception("Error in Activity 1");
        }
        catch (Exception ex)
        {
            cts.Cancel(); // Cancel all tasks
            throw; // Re-throw the exception to be caught by Task.WaitAll
        }
    }

    static void Activity2(CancellationToken token)
    {
        try
        {
        }
        catch (OperationCanceledException)
        {
            Console.WriteLine("Activity 2 cancelled.");
        }
    }

    static void Activity3(CancellationToken token)
    {
        try
        {
        }
        catch (OperationCanceledException)
        {
            Console.WriteLine("Activity 3 cancelled.");
        }
    }

    static void RevertChanges()
    {
        // Implement the logic to revert changes here
        Console.WriteLine("Reverting changes...");
    }
}

Task Paralel Library(TPL)

TPL is a set of public types and APIs in the System.Threading.Tasks namespace that allows you to easily write parallel and asynchronous code. Here are some key features and benefits of TPL:

1. Creating and Starting Tasks

using System;
using System.Threading.Tasks;

class Program
{
    static void Main(string[] args)
    {
        Task task = Task.Run(() => 
        {
            // Your code here
            Console.WriteLine("Task is running.");
        });

        task.Wait(); // Waits for the task to complete
    }
}

2. Returning Results from Tasks

using System;
using System.Threading.Tasks;

class Program
{
    static void Main(string[] args)
    {
        Task<int> task = Task.Run(() => 
        {
            // Your code here
            return 42;
        });

        int result = task.Result; // Blocks and gets the result
        Console.WriteLine($"Result: {result}");
    }
}

3. Asynchronous Programming with async and await

using System;
using System.Threading.Tasks;

class Program
{
    static async Task Main(string[] args)
    {
        int result = await GetNumberAsync();
        Console.WriteLine($"Result: {result}");
    }

    static Task<int> GetNumberAsync()
    {
        return Task.Run(() => 
        {
            // Simulate work
            Task.Delay(2000).Wait();
            return 42;
        });
    }
}

4. Parallel Programming with Parallel Class

using System;
using System.Threading.Tasks;

class Program
{
    static void Main(string[] args)
    {
        Parallel.For(0, 10, i => 
        {
            Console.WriteLine($"Processing {i}");
        });

        string[] words = { "one", "two", "three" };
        Parallel.ForEach(words, word => 
        {
            Console.WriteLine($"Processing {word}");
        });
    }
}

5. Continuation Tasks

Tasks can be chained together using continuation tasks.

using System;
using System.Threading.Tasks;

class Program
{
    static void Main(string[] args)
    {
        Task task = Task.Run(() => 
        {
            Console.WriteLine("Initial task.");
        });

        task.ContinueWith(t => 
        {
            Console.WriteLine("Continuation task.");
        }).Wait();
    }
}

6. Exception Handling in Tasks

using System;
using System.Threading.Tasks;

class Program
{
    static void Main(string[] args)
    {
        Task task = Task.Run(() => 
        {
            throw new InvalidOperationException("Something went wrong.");
        });

        try
        {
            task.Wait();
        }
        catch (AggregateException ex)
        {
            foreach (var innerEx in ex.InnerExceptions)
            {
                Console.WriteLine(innerEx.Message);
            }
        }
    }
}

7. Task.WaitAll and Task.WhenAll

Wait for multiple tasks to complete using Task.WaitAll and Task.WhenAll

using System;
using System.Threading.Tasks;

class Program
{
    static async Task Main(string[] args)
    {
        Task task1 = Task.Run(() => Task.Delay(1000));
        Task task2 = Task.Run(() => Task.Delay(2000));

        // Blocking wait
        Task.WaitAll(task1, task2);

        // Async wait
        await Task.WhenAll(task1, task2);
    }
}
  • Task.WaitAll: Blocks until all tasks complete.
  • Task.WhenAll: Returns a task that completes when all tasks complete.

8. Task.WaitAny and Task.WhenAny

Wait for any one of multiple tasks to complete using Task.WaitAny and Task.WhenAny.

using System;
using System.Threading.Tasks;

class Program
{
    static async Task Main(string[] args)
    {
        Task task1 = Task.Run(() => Task.Delay(1000));
        Task task2 = Task.Run(() => Task.Delay(2000));

        // Blocking wait
        int index = Task.WaitAny(task1, task2);
        Console.WriteLine($"Task {index + 1} completed first.");

        // Async wait
        Task firstTask = await Task.WhenAny(task1, task2);
        Console.WriteLine("First task completed.");
    }
}
  • Task.WaitAny: Blocks until any one of the tasks completes.
  • Task.WhenAny: Returns a task that completes when any one of the tasks completes.

9. Cancellation of Tasks

using System;
using System.Threading;
using System.Threading.Tasks;

class Program
{
    static async Task Main(string[] args)
    {
        CancellationTokenSource cts = new CancellationTokenSource();

        Task task = Task.Run(() => 
        {
            for (int i = 0; i < 10; i++)
            {
                if (cts.Token.IsCancellationRequested)
                {
                    Console.WriteLine("Task cancelled.");
                    return;
                }

                Task.Delay(1000).Wait();
                Console.WriteLine($"Task running {i}");
            }
        }, cts.Token);

        await Task.Delay(3000);
        cts.Cancel();
        await task;
    }
}

Latest Press Releases

  • /C O R R E C T I O N — Elevation Point/

    January 13, 2026
  • Jazz in the Gardens Just Got Bigger: Legends Added to the 2026 Lineup

    January 13, 2026
  • 123Invent Inventor Develops the Saving Lives Shield (TKI-1259)

    January 13, 2026
  • Milestone Environmental Strengthens Permian Basin Leadership with Acquisition of Striker Slurry Injection Facility

    January 13, 2026
  • /C O R R E C T I O N — Tripadvisor/

    January 13, 2026

Latest Lifestyle

  • [From Japan] An Unprecedented Skin Beauty Experience Just by Showering.

    January 16, 2026
  • Xamarin Forms To Maui Migration

    January 16, 2026
  • Asynchronous Multi Threaded and Parallel Programming in .NET

    January 16, 2026
  • Optimize Kids’ Closets With Smart Decluttering for a New Year Refresh

    January 16, 2026
  • The Difference Between Cleaning, Sanitizing, and Disinfecting

    January 16, 2026
  • Converting HTML To PDF With JsReport In .NET

    January 16, 2026

More from NEWSnet

Top Stories

  • Regional Collaboration Aims to Empower Youth and Faith-Based Initiatives in Kentucky and Indiana

  • Editorial Feature Highlights Sunil Kanojiya’s Background in Analytics and Project Coordination

  • Michigan-Based Dustout Air Duct Cleaning Highlights Critical Need for Professional Duct Sealing as Energy Costs Rise

  • AI and network science for emotional risk measurement in stock markets

  • OmniWatch Analysis Finds Identity Theft Rates At All-Time High, According To New FTC Data

Latest News

  • Stradimark Launches Advanced AI Marketing Tools for 2026


  • New Free Training Initiative Launched for Construction Businesses in 2026


  • Discover Personalized Care with Encino’s Top Cosmetic Dentist


  • Brothers Motors LLC Expands Inventory to Serve Customers Better


  • First Class Trucking Strengthens Nationwide Freight Services


In Case You Missed It

  • Stradimark Launches Advanced AI Marketing Tools for 2026


  • New Free Training Initiative Launched for Construction Businesses in 2026


  • Discover Personalized Care with Encino’s Top Cosmetic Dentist


  • Brothers Motors LLC Expands Inventory to Serve Customers Better


  • First Class Trucking Strengthens Nationwide Freight Services


All content ©2025 Bridge News LLC
All Rights Reserved. For more information on this site, please read our Privacy Policy, Terms of Service, and Ad Choices.