Skip to content

intisor/MultiThread

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

7 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

MultiThread Practice - C# Multithreading and Asynchronous Programming

A comprehensive educational project demonstrating advanced multithreading, asynchronous programming, and concurrency concepts in C# using .NET 8.0. This repository contains practical examples organized into modules, each focusing on specific threading and async patterns.

πŸ“‹ Table of Contents

🎯 Overview

This project serves as a comprehensive learning resource for understanding multithreading and asynchronous programming in C#. It progresses from basic thread operations to advanced async patterns, providing hands-on examples of:

  • Thread Management: Creating, starting, joining, and cancelling threads
  • Thread Synchronization: Locks, mutexes, semaphores, and monitors
  • Thread Pooling: Efficient thread management using ThreadPool
  • Asynchronous Programming: async/await, Tasks, and ValueTask
  • Advanced Patterns: TAP (Task-based Asynchronous Pattern), async streams, and async initialization
  • Best Practices: Deadlock prevention, exception handling, and performance optimization

πŸ”§ Prerequisites

  • .NET 8.0 SDK or later
  • Visual Studio 2022 (recommended) or Visual Studio Code with C# extension
  • Basic understanding of C# programming
  • Familiarity with object-oriented programming concepts

πŸ“₯ Installation

  1. Clone the repository

    git clone https://github.com/intisor/MultiThread.git
    cd MultiThread
  2. Restore dependencies

    dotnet restore
  3. Build the solution

    dotnet build

πŸ“ Project Structure

MultiThread/
β”œβ”€β”€ Program.cs                 # Main program with delegate-based thread communication
β”œβ”€β”€ MultiThreadPrac.csproj    # Main project file
β”œβ”€β”€ MultiThreadPrac.sln       # Solution file
β”œβ”€β”€ Module1/                  # Hello World starter
β”œβ”€β”€ Module2/                  # Thread joining and cancellation
β”œβ”€β”€ Module3/                  # Thread synchronization
β”œβ”€β”€ Module4/                  # ThreadPool implementation
β”œβ”€β”€ Module5/                  # Async/Await fundamentals
β”œβ”€β”€ Module6/                  # Async streams
β”œβ”€β”€ Module7/                  # Task methods and properties
β”œβ”€β”€ Module8/                  # ValueTask optimization
β”œβ”€β”€ Module9/                  # Task-based Asynchronous Pattern (TAP)
└── Module10/                 # Deadlock prevention and best practices

Each module is a self-contained C# console application targeting .NET 8.0.

πŸ“š Modules Overview

Main Program - Delegate-Based Thread Communication

Location: Program.cs

Concept: Demonstrates how to retrieve data from a thread function using delegates.

Key Features:

  • Custom delegate definition (SumOfNumberCallDelegate)
  • Thread callback mechanism
  • Passing data between threads via delegates
  • Helper class pattern for thread management

What it does:

  • Calculates cumulative sum of numbers (0 to n-1)
  • Reports each intermediate sum back to the main thread via delegate callback
  • Demonstrates decoupling between thread execution and result consumption

Code Highlights:

// Delegate for thread communication
public delegate void SumOfNumberCallDelegate(int SumOfNum);

// Helper class manages thread work and callbacks
public class Helper
{
    private int Number;
    SumOfNumberCallDelegate _CallDelegate;
    
    public void ShowNumbers()
    {
        int sum = 0;
        for (var i = 0; i < Number; i++)
        {
            sum += i;
            _CallDelegate?.Invoke(sum);  // Callback to main thread
        }
    }
}

Run:

dotnet run --project MultiThreadPrac.csproj

Module 1 - Getting Started

Location: Module1/Program.cs

Concept: Basic "Hello, World!" introduction to the project structure.

Purpose: Serves as a template and starting point for understanding the module structure.

Run:

dotnet run --project Module1/Module1.csproj

Module 2 - Thread Joining and Cancellation

Location: Module2/Program.cs

Concept: Advanced thread lifecycle management with joining and cancellation tokens.

Key Features:

  • CancellationToken and CancellationTokenSource usage
  • Thread joining with Join() method
  • Synchronized vs asynchronous thread execution
  • Handling cancellation requests gracefully

What it demonstrates:

  • Creating multiple threads with different start methods
  • Using CancellationToken to signal thread cancellation
  • Join() to wait for thread completion before proceeding
  • Ordinal number formatting helper method

Key Code:

CancellationTokenSource cts = new CancellationTokenSource();
CancellationToken token = cts.Token;

Thread t1 = new Thread(new ParameterizedThreadStart(Method1));
t1.Start(token);

// Cancel the token
cts.Cancel();

// Wait for t2 to complete before starting t3
t2.Join();

Run:

dotnet run --project Module2/Module2.csproj

Module 3 - Thread Synchronization

Location: Module3/Program.cs

Concept: Securing shared resources using various synchronization mechanisms.

Key Features:

  • Lock: Simple mutual exclusion
  • Semaphore: Controlling access to a limited number of resources
  • Monitor: Advanced synchronization with wait/pulse capabilities
  • Mutex: Cross-process synchronization
  • ReaderWriterLockSlim: Optimized read-heavy scenarios

What it demonstrates:

  • Protecting shared counter variable from race conditions
  • Using lock keyword for critical sections
  • Semaphore to limit concurrent thread access (2 threads at a time)
  • Thread naming and identification
  • Preventing data corruption in multithreaded environments

Key Code:

private static int _counter = 0;
private static readonly object _lock = new object();
private static Semaphore _semaphore = new Semaphore(2, 2); // Max 2 concurrent threads

private static void IncrementCounter()
{
    _semaphore.WaitOne();  // Acquire semaphore
    for (int i = 1; i <= 5; i++)
    {
        lock (_lock)  // Protect shared resource
        {
            _counter++;
        }
        Console.WriteLine($"{Thread.CurrentThread.Name} : {i}");
    }
    _semaphore.Release();  // Release semaphore
}

Run:

dotnet run --project Module3/Module3.csproj

Module 4 - ThreadPool

Location: Module4/Program.cs

Concept: Efficient thread management using the .NET ThreadPool.

Key Features:

  • Configuring ThreadPool min/max threads
  • Queueing work items to ThreadPool
  • Automatic thread reuse and management
  • Performance benefits over manual thread creation

What it demonstrates:

  • Reading and setting ThreadPool configuration
  • ThreadPool.QueueUserWorkItem() for task scheduling
  • Thread pool thread identification
  • Simulating concurrent work with random delays

Key Code:

// Configure thread pool
ThreadPool.SetMaxThreads(10, maxThreads);
ThreadPool.SetMinThreads(7, minThreads);

// Queue tasks
for (int i = 1; i < 20; i++)
{
    int taskNumber = i;
    ThreadPool.QueueUserWorkItem(Task, taskNumber);
}

static void Task(object state)
{
    int taskNumber = (int)state;
    Console.WriteLine($"Task {taskNumber} on thread {Thread.CurrentThread.ManagedThreadId}");
    Thread.Sleep(new Random().Next(100, 1000));
}

Run:

dotnet run --project Module4/Module4.csproj

Module 5 - Async/Await Fundamentals

Location: Module5/Program.cs

Concept: Introduction to asynchronous programming with async/await pattern.

Key Features:

  • async Task method declaration
  • await keyword for asynchronous operations
  • Task.Delay() for non-blocking delays
  • Exception handling in async methods
  • Async method chaining

What it demonstrates:

  • Making coffee asynchronously (heating water, mixing ingredients)
  • Sequential async operations with proper awaiting
  • Try-catch exception handling in async context
  • Returning values from async methods

Key Code:

private static async Task Main(string[] args)
{
    try
    {
        Coffee coffee = new Coffee();
        string heatwater = await coffee.HeatWaterAsync(2);
        Console.WriteLine(heatwater);
        await coffee.MixAsync(2, 4);
        Console.WriteLine("Breakfast Completed");
    }
    catch (Exception ex)
    {
        Console.WriteLine($"An error occurred: {ex.Message}");
    }
}

public async Task<string> HeatWaterAsync(int temp)
{
    Console.WriteLine($"Heating water at {temp}Β°C");
    await Task.Delay(3000);  // Non-blocking delay
    return "Water has been heated";
}

Run:

dotnet run --project Module5/Module5.csproj

Module 6 - Async Streams

Location: Module6/Program.cs

Concept: Asynchronous iteration using IAsyncEnumerable<T> and await foreach.

Key Features:

  • IAsyncEnumerable<T> for async sequences
  • yield return in async methods
  • await foreach for consuming async streams
  • Streaming data production and consumption

What it demonstrates:

  • Producing numbers asynchronously with delays
  • Consumer-producer pattern with async streams
  • Memory-efficient streaming of data
  • Asynchronous iteration pattern

Key Code:

public async IAsyncEnumerable<int> ProduceNumberAsync()
{
    for (var i = 1; i <= 10; i++)
    {
        await Task.Delay(5000);  // Simulate async work
        yield return i;           // Stream each number
    }
}

public async Task ConsumeNumberAsync()
{
    await foreach (var item in ProduceNumberAsync())
    {
        Console.WriteLine(item);  // Process each item as it arrives
    }
}

Run:

dotnet run --project Module6/Module6.csproj

Module 7 - Task Methods and Properties

Location: Module7/Program.cs

Concept: Comprehensive overview of Task API methods and properties.

Key Features:

  • Task.FromResult() - Creating completed tasks
  • Task.Run() - Running work on thread pool
  • Task.Delay() - Non-blocking delays
  • Task.WhenAny() - Waiting for first completion
  • Task.WaitAll() - Waiting for all tasks
  • Task.ContinueWith() - Task continuation
  • Exception handling in tasks

What it demonstrates:

  • Different ways to create and manage Tasks
  • Task composition and coordination
  • Synchronous vs asynchronous waiting
  • Task exception propagation

Key Code:

// Completed task
Task<int> completedTask = Task.FromResult(42);

// Long-running task
Task longRunningTask = Task.Run(LongRunningOperation);

// Wait for first to complete
Task firstToFinish = await Task.WhenAny(completedTask, longRunningTask);

// Wait for all
Task.WaitAll([completedTask, longRunningTask]);

// Continuation
longRunningTask.ContinueWith(t => Console.WriteLine("Continued..."));

// Exception handling
try
{
    await Task.Run(() => throw new Exception("Task exception!"));
}
catch (Exception ex)
{
    Console.WriteLine($"Caught: {ex.Message}");
}

Run:

dotnet run --project Module7/Module7.csproj

Module 8 - ValueTask Optimization

Location: Module8/Program.cs

Concept: Performance optimization using ValueTask<T> for potentially synchronous operations.

Key Features:

  • ValueTask<T> vs Task<T> comparison
  • Caching pattern implementation
  • Avoiding Task allocation overhead
  • Synchronous completion optimization

What it demonstrates:

  • Using ValueTask for cache hits (synchronous path)
  • Falling back to Task for cache misses (asynchronous path)
  • Performance benefits of value types
  • Dictionary-based caching pattern

Key Code:

private readonly Dictionary<int, User> _cache = new Dictionary<int, User>();

public ValueTask<User> GetUserAsync(int userId)
{
    // Cache hit - return immediately without Task allocation
    if (_cache.TryGetValue(userId, out User cachedUser))
    {
        return new ValueTask<User>(cachedUser);
    }
    else
    {
        // Cache miss - fetch asynchronously
        return new ValueTask<User>(FetchUserFromDatabaseAsync(userId));
    }
}

private async Task<User> FetchUserFromDatabaseAsync(int userId)
{
    await Task.Delay(1000);  // Simulate I/O
    var user = new User { Id = userId, Name = "John Doe" };
    _cache.Add(userId, user);
    return user;
}

Run:

dotnet run --project Module8/Module8.csproj

Module 9 - Task-Based Asynchronous Pattern (TAP)

Location: Module9/Program.cs

Concept: Design patterns for asynchronous initialization and object creation.

Key Features:

  • Async Factory Method Pattern - Creating objects asynchronously
  • Async Singleton Pattern - Thread-safe singleton with async initialization
  • Asynchronous Initialization Pattern - Complex object setup

What it demonstrates:

  • Alternative to constructors for async initialization
  • Thread-safe lazy initialization with Tasks
  • Separation of object creation from initialization
  • Real-world patterns for async object lifecycle

Key Code:

// Async Factory Method
public class ReportFactory
{
    public static async Task<Report> CreateAsync(string title)
    {
        await Task.Delay(1000);  // Simulate async setup
        return new Report { Title = title };
    }
}

// Async Singleton
public class Configuration
{
    private static Task<Configuration> instanceTask;
    
    public static Task<Configuration> GetInstanceAsync()
    {
        if (instanceTask == null)
        {
            instanceTask = InitializeAsync();
        }
        return instanceTask;
    }
    
    private static async Task<Configuration> InitializeAsync()
    {
        await Task.Delay(1000);
        return new Configuration();
    }
}

// Usage
var report = await ReportFactory.CreateAsync("Report Title");
var config = await Configuration.GetInstanceAsync();
var logger = await Logger.InitializeAsync();

Run:

dotnet run --project Module9/Module9.csproj

Module 10 - Deadlock Prevention and Best Practices

Location: Module10/Program.cs

Concept: Best practices for async programming and deadlock prevention.

Key Principles Covered:

  1. Avoid Async Void

    • Use async Task instead of async void (except for event handlers)
    • Different error-handling semantics prevent unobserved exceptions
  2. Configure Context

    • Use ConfigureAwait(false) in library code
    • Prevents deadlocks by not capturing SynchronizationContext
  3. Understand Synchronization Context

    • How continuations are posted back to main thread
    • Avoiding blocked contexts while awaiting
  4. Use Task.Run for CPU-bound Work

    • Keep main thread responsive
    • Offload CPU-intensive work to thread pool
  5. Handle Exceptions Properly

    • Exceptions return as Faulted tasks
    • Always await or inspect Task.Exception
  6. Avoid Blocking Calls

    • Never use Task.Result or Task.Wait()
    • Always use await for async waiting
  7. Monitor Deadlocks

    • Follow "async all the way" principle
    • Review code for blocking calls on async methods

Key Code:

// Good: Async Task method
public static async Task<string> ExceptionAsync()
{
    await Task.Delay(5000);
    return "Exceptions thrown in async method return as Faulted task";
}

// Good: Avoid blocking
public static async Task BlockAsync()
{
    await Task.Delay(5000);  // Instead of Task.Result or Task.Wait()
    Console.WriteLine("Avoid blocking calls");
}

// Usage
await ExceptionAsync();  // Good: Using await
await BlockAsync();      // Good: Async all the way

Run:

dotnet run --project Module10/Module10.csproj

πŸ”‘ Key Concepts Covered

Threading Fundamentals

  • Thread creation and lifecycle
  • Thread states and transitions
  • Thread naming and identification
  • ParameterizedThreadStart vs ThreadStart
  • Thread joining and synchronization

Thread Synchronization

  • Lock: Mutual exclusion for critical sections
  • Monitor: Advanced synchronization with Wait/Pulse
  • Mutex: Cross-process synchronization
  • Semaphore: Limiting concurrent access
  • ReaderWriterLockSlim: Optimized read/write scenarios

Thread Management

  • Manual thread creation vs ThreadPool
  • ThreadPool configuration and optimization
  • Work item queuing
  • Thread pool sizing strategies

Asynchronous Programming

  • async/await syntax and semantics
  • Task and Task types
  • ValueTask for performance optimization
  • Async streams with IAsyncEnumerable
  • Exception propagation in async methods

Advanced Patterns

  • Task-based Asynchronous Pattern (TAP)
  • Async factory methods
  • Async singleton pattern
  • Async initialization pattern
  • Delegate-based thread communication

Best Practices

  • Deadlock prevention techniques
  • ConfigureAwait usage
  • Synchronization context understanding
  • Proper exception handling
  • "Async all the way" principle
  • CPU-bound vs I/O-bound work separation

πŸš€ Running the Examples

Run the Main Program

dotnet run --project MultiThreadPrac.csproj

Run a Specific Module

# Module 2 - Thread Joining and Cancellation
dotnet run --project Module2/Module2.csproj

# Module 5 - Async/Await
dotnet run --project Module5/Module5.csproj

# Module 10 - Best Practices
dotnet run --project Module10/Module10.csproj

Build All Projects

dotnet build MultiThreadPrac.sln

Run with Debugging

  1. Open the solution in Visual Studio or VS Code
  2. Set the desired project as startup project
  3. Press F5 to run with debugging
  4. Use breakpoints to step through thread execution

βœ… Best Practices Demonstrated

1. Thread Safety

  • Always protect shared resources with appropriate synchronization
  • Use the simplest synchronization mechanism that meets your needs
  • Minimize the scope of locks to avoid contention

2. Async Programming

  • Use async/await for I/O-bound operations
  • Avoid async void except for event handlers
  • Always await async methods ("async all the way")
  • Use ConfigureAwait(false) in library code

3. Resource Management

  • Properly dispose of synchronization primitives (CancellationTokenSource, Semaphore)
  • Use thread pool for short-lived tasks
  • Create manual threads only for long-running operations

4. Error Handling

  • Wrap async operations in try-catch blocks
  • Observe task exceptions to prevent silent failures
  • Use CancellationToken for cooperative cancellation

5. Performance

  • Use ValueTask when operations may complete synchronously
  • Leverage ThreadPool instead of creating threads manually
  • Minimize context switches and lock contention

6. Code Organization

  • Separate synchronous and asynchronous code paths clearly
  • Use meaningful names for threads for debugging
  • Keep async method signatures consistent (Task or Task)

🀝 Contributing

Contributions are welcome! If you'd like to add more examples or improve existing ones:

  1. Fork the repository
  2. Create a feature branch (git checkout -b feature/new-module)
  3. Add your module following the existing pattern:
    • Create a new ModuleX folder
    • Add a self-contained Program.cs
    • Include comments explaining the concept
    • Add a ModuleX.csproj file
  4. Update this README with module documentation
  5. Commit your changes (git commit -am 'Add Module X: Description')
  6. Push to the branch (git push origin feature/new-module)
  7. Create a Pull Request

Module Template

Each module should follow this structure:

  • Clear comments at the top explaining the concept
  • Self-contained, runnable example
  • Console output showing the behavior
  • Demonstrates one primary concept
  • Includes error handling where appropriate

πŸ“„ License

This project is provided as-is for educational purposes. Feel free to use, modify, and distribute the code as you see fit.

πŸ“š Further Reading

Official Documentation

Recommended Books

  • "Concurrency in C# Cookbook" by Stephen Cleary
  • "C# 10 in a Nutshell" by Joseph Albahari
  • "CLR via C#" by Jeffrey Richter

Advanced Topics


Note: This is a practice repository created for learning purposes. The examples demonstrate various threading and async patterns in isolation. In production code, always consider the specific requirements and constraints of your application.

Framework: .NET 8.0
Language: C# 12
Author: Educational Project
Last Updated: 2025

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Contributors 2

  •  
  •  

Languages