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

7 1 1

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

🚀

Sentry image

Hands-on debugging session: instrument, monitor, and fix

Join Lazar for a hands-on session where you’ll build it, break it, debug it, and fix it. You’ll set up Sentry, track errors, use Session Replay and Tracing, and leverage some good ol’ AI to find and fix issues fast.

RSVP here →

Top comments (1)

Collapse
 
ngtduc693 profile image
Duc Nguyen Thanh

good topic

Heroku

This site is built on Heroku

Join the ranks of developers at Salesforce, Airbase, DEV, and more who deploy their mission critical applications on Heroku. Sign up today and launch your first app!

Get Started