*
Previous Attributes in C# Task Parallel Library (TPL) in C# Next

🧵 Threading & Multithreading in C#

🧵 Threading & Multithreading in C#

Multithreading in C#

In C#, a thread is a single sequence of instructions executed by a program. Threading is the practice of managing these individual execution paths. A standard C# application begins with a single, primary thread that performs all operations sequentially. Multithreading is the ability to run two or more threads concurrently within a single program.

This improves performance and responsiveness, allowing tasks to run in the background while the main thread handles user interactions.

Key Concepts of Multithreading

1. Concurrency vs. Parallelism

  • Concurrency: Managing multiple tasks at once. On a single-core machine, threads are time-sliced to create the illusion of simultaneous execution.
  • Parallelism: Executing multiple tasks simultaneously on multiple cores.

2. Modern vs. Classic Threading

Feature Modern approach (Tasks/async/await) Classic approach (Thread class)
Abstraction Higher-level. A Task represents an asynchronous operation, not a physical thread. Low-level. You manually create and manage each Thread object.
Efficiency Tasks use a thread pool to reuse threads, reducing overhead. Creating new Thread objects is expensive.
Resource Optimized for CPU-bound and I/O-bound work. Best suited for long-running, dedicated tasks.
Error handling Tasks aggregate exceptions; await simplifies handling. Exceptions must be handled explicitly inside the thread.
Cancellation Uses CancellationToken for safe cancellation. Thread.Abort() is unsafe and obsolete.

3. Synchronization

When multiple threads access shared data, race conditions can occur. To prevent this, C# provides several synchronization primitives:

  • lock: Ensures only one thread executes a critical section at a time.
  • Mutex: Synchronizes access across processes.
  • SemaphoreSlim: Limits concurrent access to a resource.
  • Interlocked: Provides atomic operations for simple variables.
  • Concurrent Collections: Thread-safe collections like ConcurrentDictionary.

When to Use Threading

  • Responsive UI: Offload long-running tasks to background threads.
  • Parallel Computing: Split CPU-intensive tasks across multiple cores.
  • Server Applications: Handle multiple client requests concurrently.

Example: Asynchronous Programming with async/await

This modern approach is recommended for most multithreading scenarios, especially I/O-bound operations.

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

class Program
{
    public static int PerformCpuWork()
    {
        Console.WriteLine("CPU-bound work starting...");
        Thread.Sleep(3000);
        Console.WriteLine("CPU-bound work finished.");
        return 42;
    }

    public static async Task<string> PerformIoWorkAsync()
    {
        Console.WriteLine("I/O-bound work starting...");
        await Task.Delay(2000);
        Console.WriteLine("I/O-bound work finished.");
        return "Data from I/O";
    }

    public static async Task Main()
    {
        Console.WriteLine("Main thread: Program started.");

        Task<int> cpuTask = Task.Run(() => PerformCpuWork());
        Task<string> ioTask = PerformIoWorkAsync();

        Console.WriteLine("Main thread: Continuing with other tasks...");

        string ioResult = await ioTask;
        int cpuResult = await cpuTask;

        Console.WriteLine($"\nMain thread: Received I/O result: {ioResult}");
        Console.WriteLine($"Main thread: Received CPU result: {cpuResult}");
        Console.WriteLine("Main thread: Program finished.");
    }
}

Threading in C# allows your application to perform multiple tasks concurrently. A thread is the smallest unit of execution, and multithreading enables multiple threads to run in parallel, improving responsiveness and performance.

🔧 What is a Thread?

  • A thread is a path of execution within a process.
  • Every C# application starts with a single main thread.
  • Additional threads can be created to perform tasks concurrently.

🔀 What is Multithreading?

  • Multithreading allows multiple threads to run simultaneously.
  • Useful for tasks like file I/O, network calls, or UI updates.
  • Improves performance and keeps applications responsive.

🧪 Example: Basic Thread

  using System;
  using System.Threading;

  class Program {
      static void PrintNumbers() {
          for (int i = 1; i <= 5; i++) {
              Console.WriteLine("Worker Thread: " + i);
              Thread.Sleep(500);
          }
      }

      static void Main() {
          Thread t1 = new Thread(PrintNumbers);
          t1.Start();

          for (int i = 1; i <= 5; i++) {
              Console.WriteLine("Main Thread: " + i);
              Thread.Sleep(500);
          }
      }
  }
  

🧵 Creating Threads

  • Thread Class: Basic way to create threads.
  • ParameterizedThreadStart: Pass arguments to threads.
  • Lambda Expressions: Inline thread logic.
  • Task Parallel Library (TPL): Modern, scalable threading.
  • async/await: Simplifies asynchronous programming.

🔁 Example: Using Task

  using System.Threading.Tasks;

  Task.Run(() => {
      Console.WriteLine("Running in a separate thread");
  });
  

🔐 Thread Synchronization

  • Use lock to prevent race conditions.
  • Use Monitor, Mutex, or Semaphore for advanced control.
  • Always protect shared resources accessed by multiple threads.

✅ Best Practices

  • Use Task and async/await for modern multithreading.
  • Avoid blocking the main thread—use background threads.
  • Keep thread logic short and efficient.
  • Use thread-safe collections like ConcurrentDictionary.
  • Always handle exceptions inside threads.

📌 When to Use

  • For long-running or CPU-bound tasks.
  • To keep UI responsive in desktop/mobile apps.
  • For parallel processing of data.
  • In server apps to handle multiple requests concurrently.

🚫 When Not to Use

  • For simple, sequential tasks—threads add complexity.
  • When thread overhead outweighs performance gains.
  • Without proper synchronization—can lead to bugs.

⚠️ Precautions

  • Watch out for deadlocks and race conditions.
  • Don’t create too many threads—use thread pools.
  • Use cancellation tokens to stop threads gracefully.
  • Monitor thread usage to avoid resource exhaustion.

🎯 Advantages

  • Responsiveness: Keeps UI and services fluid.
  • Performance: Utilizes CPU cores efficiently.
  • Scalability: Handles multiple tasks concurrently.
  • Flexibility: Supports both low-level and high-level threading models.

📝 Conclusion

Threading and multithreading in C# are essential for building responsive, high-performance applications. By understanding how threads work and applying best practices, you can unlock powerful concurrency features while avoiding common pitfalls.

Back to Index
Previous Attributes in C# Task Parallel Library (TPL) in C# Next
*
*