DEV Community

Cover image for Concurrency and Parallel Programming in C#: a detailed hands-on exercise plan
Taki (Kieu Dang)
Taki (Kieu Dang)

Posted on

Concurrency and Parallel Programming in C#: a detailed hands-on exercise plan

Here’s a detailed hands-on exercise plan for mastering Concurrency and Parallel Programming in C# (2025). Each phase includes concepts, code examples, and exercises to help you solidify your skills.


🚀 Phase 1: Fundamentals of Multithreading in C#

Concepts to Learn:

✔ Understanding Threads and how the Thread class works

✔ Managing Thread Lifecycle (Start, Sleep, Join, Abort)

Thread Synchronization (lock, Monitor, Mutex, Semaphore, AutoResetEvent, ManualResetEvent)

✔ Avoiding Race Conditions & Deadlocks

Exercise 1: Creating a Simple Thread

📌 Task: Create a new thread that prints numbers from 1 to 10.

Goal: Understand how to create and start a thread.

using System;
using System.Threading;

class Program
{
    static void PrintNumbers()
    {
        for (int i = 1; i <= 10; i++)
        {
            Console.WriteLine($"Thread: {i}");
            Thread.Sleep(500); // Simulate work
        }
    }

    static void Main()
    {
        Thread thread = new Thread(PrintNumbers);
        thread.Start();
        thread.Join(); // Wait for thread to finish
        Console.WriteLine("Main Thread Exiting...");
    }
}
Enter fullscreen mode Exit fullscreen mode

Exercise 2: Thread Synchronization with lock

📌 Task: Create two threads that increment a shared counter.

Goal: Prevent race conditions using lock.

using System;
using System.Threading;

class Program
{
    static int counter = 0;
    static object lockObject = new object();

    static void Increment()
    {
        for (int i = 0; i < 1000; i++)
        {
            lock (lockObject)  // Prevent race condition
            {
                counter++;
            }
        }
    }

    static void Main()
    {
        Thread t1 = new Thread(Increment);
        Thread t2 = new Thread(Increment);

        t1.Start();
        t2.Start();
        t1.Join();
        t2.Join();

        Console.WriteLine($"Final Counter: {counter}");
    }
}
Enter fullscreen mode Exit fullscreen mode

Exercise 3: Avoiding Deadlocks

📌 Task: Simulate a deadlock and fix it using proper lock ordering.

Goal: Learn deadlock prevention strategies.

using System;
using System.Threading;

class Program
{
    static object lock1 = new object();
    static object lock2 = new object();

    static void Thread1()
    {
        lock (lock1)
        {
            Thread.Sleep(100);
            lock (lock2)
            {
                Console.WriteLine("Thread1 acquired both locks.");
            }
        }
    }

    static void Thread2()
    {
        lock (lock2)
        {
            Thread.Sleep(100);
            lock (lock1)  // Reversed order causes deadlock
            {
                Console.WriteLine("Thread2 acquired both locks.");
            }
        }
    }

    static void Main()
    {
        Thread t1 = new Thread(Thread1);
        Thread t2 = new Thread(Thread2);

        t1.Start();
        t2.Start();
        t1.Join();
        t2.Join();
    }
}
Enter fullscreen mode Exit fullscreen mode

🔹 Fix: Ensure both threads lock objects in the same order.


🚀 Phase 2: Task-Based Asynchronous Programming (TAP)

Concepts to Learn:

✔ Understanding Task and Task.Run()

✔ Using ContinueWith() for task chaining

✔ Implementing Parallel.ForEach and Parallel.Invoke


Exercise 1: Running Tasks in Parallel

📌 Task: Run multiple tasks concurrently.

Goal: Understand Task.Run().

using System;
using System.Threading.Tasks;

class Program
{
    static void TaskMethod()
    {
        Console.WriteLine($"Task Running on Thread {Task.CurrentId}");
    }

    static void Main()
    {
        Task t1 = Task.Run(TaskMethod);
        Task t2 = Task.Run(TaskMethod);
        Task.WaitAll(t1, t2);

        Console.WriteLine("All tasks completed.");
    }
}
Enter fullscreen mode Exit fullscreen mode

Exercise 2: Using Parallel.ForEach

📌 Task: Process a list of numbers using Parallel.ForEach().

Goal: Use PLINQ for faster processing.

using System;
using System.Collections.Generic;
using System.Threading.Tasks;

class Program
{
    static void Main()
    {
        List<int> numbers = new List<int> { 1, 2, 3, 4, 5 };

        Parallel.ForEach(numbers, number =>
        {
            Console.WriteLine($"Processing {number} on thread {Task.CurrentId}");
        });

        Console.WriteLine("Processing complete.");
    }
}
Enter fullscreen mode Exit fullscreen mode

🚀 Phase 3: Asynchronous Programming with async/await

Concepts to Learn:

Async/Await Basics

Task.Delay(), ConfigureAwait(false)

Handling Exceptions in Async Methods


Exercise 1: Making an Async HTTP Request

📌 Task: Fetch data from an API asynchronously.

Goal: Learn awaiting I/O-bound operations.

using System;
using System.Net.Http;
using System.Threading.Tasks;

class Program
{
    static async Task FetchDataAsync()
    {
        using HttpClient client = new HttpClient();
        string data = await client.GetStringAsync("https://jsonplaceholder.typicode.com/posts/1");
        Console.WriteLine(data);
    }

    static async Task Main()
    {
        await FetchDataAsync();
    }
}
Enter fullscreen mode Exit fullscreen mode

Exercise 2: Async File Read

📌 Task: Read a file asynchronously.

Goal: Learn I/O-bound async operations.

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

class Program
{
    static async Task ReadFileAsync(string path)
    {
        using StreamReader reader = new StreamReader(path);
        string content = await reader.ReadToEndAsync();
        Console.WriteLine(content);
    }

    static async Task Main()
    {
        await ReadFileAsync("test.txt");
    }
}
Enter fullscreen mode Exit fullscreen mode

🚀 Phase 4: Data Parallelism & PLINQ

Exercise: Parallel LINQ (PLINQ)

📌 Task: Filter large datasets using PLINQ.

Goal: Learn parallelized LINQ queries.

using System;
using System.Linq;

class Program
{
    static void Main()
    {
        var numbers = Enumerable.Range(1, 100000);
        var evenNumbers = numbers.AsParallel().Where(n => n % 2 == 0).ToList();

        Console.WriteLine($"Total even numbers: {evenNumbers.Count}");
    }
}
Enter fullscreen mode Exit fullscreen mode

🚀 Phase 5: Advanced Concurrency

Exercise: Producer-Consumer Pattern

📌 Task: Implement a producer-consumer pattern using BlockingCollection.

Goal: Learn thread-safe queue processing.

using System;
using System.Collections.Concurrent;
using System.Threading;
using System.Threading.Tasks;

class Program
{
    static BlockingCollection<int> queue = new BlockingCollection<int>();

    static void Producer()
    {
        for (int i = 0; i < 5; i++)
        {
            queue.Add(i);
            Console.WriteLine($"Produced: {i}");
            Thread.Sleep(100);
        }
        queue.CompleteAdding();
    }

    static void Consumer()
    {
        foreach (var item in queue.GetConsumingEnumerable())
        {
            Console.WriteLine($"Consumed: {item}");
        }
    }

    static void Main()
    {
        Task.Run(Producer);
        Task.Run(Consumer).Wait();
    }
}
Enter fullscreen mode Exit fullscreen mode

Final Step: Real-World Projects

✔ Build a high-performance API

✔ Optimize concurrent processing in microservices

✔ Implement parallel image processing

🚀

Top comments (0)