đŸ§ĩ Deep Dive into Multithreading in C# – Part 1: Basics, Pitfalls, and Why It Matters

image

Introduction

āφāϜāϕ⧇āϰ āϏāĻĢāϟāĻ“ā§Ÿā§āϝāĻžāϰ āĻĻ⧁āύāĻŋ⧟āĻžā§Ÿ speed āĻāĻŦāĻ‚ responsiveness āϖ⧁āĻŦāχ āϗ⧁āϰ⧁āĻ¤ā§āĻŦāĻĒā§‚āĻ°ā§āĻŖāĨ¤ āĻŦā§āϝāĻŦāĻšāĻžāϰāĻ•āĻžāϰ⧀āϰāĻž āϚāĻžā§Ÿ, āϤāĻžāĻĻ⧇āϰ āĻ…ā§āϝāĻžāĻĒā§āϞāĻŋāϕ⧇āĻļāύ āϝ⧇āύ āĻāĻ•āχ āϏāĻŽā§Ÿā§‡ āĻāĻ•āĻžāϧāĻŋāĻ• āĻ•āĻžāϜ āĻ•āϰāϤ⧇ āĻĒāĻžāϰ⧇āĨ¤ āϝ⧇āĻŽāĻ¨â€”

  • āĻ“ā§Ÿā§‡āĻŦ āϏāĻžāĻ°ā§āĻ­āĻžāϰ āĻāĻ•āχāϏāĻžāĻĨ⧇ āĻšāĻžāϜāĻžāϰ⧋ āχāωāϜāĻžāϰ⧇āϰ request āĻšā§āϝāĻžāĻ¨ā§āĻĄā§‡āϞ āĻ•āϰāĻŦ⧇
  • āĻŽā§‹āĻŦāĻžāχāϞ āĻ…ā§āϝāĻžāĻĒ āĻĢāĻžāχāϞ āĻĄāĻžāωāύāϞ⧋āĻĄ āĻ•āϰāĻžāϰ āϏāĻŽā§ŸāĻ“ responsive āĻĨāĻžāĻ•āĻŦ⧇
  • Desktop UI āĻ…ā§āϝāĻžāĻĒ āĻŦā§āϝāĻžāĻ•āĻ—ā§āϰāĻžāωāĻ¨ā§āĻĄā§‡ calculation āĻ•āϰāϞ⧇āĻ“ āĻĢā§āϰāĻŋāϜ āĻ•āϰāĻŦ⧇ āύāĻž

āĻāχ āϏāĻŦāĻ•āĻŋāϛ⧁āϰ āϜāĻ¨ā§āϝāχ Multithreading āĻĻāϰāĻ•āĻžāϰāĨ¤ C# āĻāĻŦāĻ‚ .NET Framework āĻ multithreading āĻāϤ āĻļāĻ•ā§āϤāĻŋāĻļāĻžāϞ⧀āĻ­āĻžāĻŦ⧇ āϤ⧈āϰāĻŋ āĻ•āϰāĻž āĻšā§Ÿā§‡āϛ⧇ āϝ⧇ āϛ⧋āϟ āĻĨ⧇āϕ⧇ āĻŦ⧜ āϏāĻŦ āϧāϰāϪ⧇āϰ āϏāĻŋāĻ¸ā§āĻŸā§‡āĻŽā§‡āχ āĻŦā§āϝāĻŦāĻšāĻžāϰ āĻ•āϰāĻž āϝāĻžā§ŸāĨ¤

Multithreading vs Multiprocessing

āĻĒā§āϰāĻĨāĻŽā§‡āχ āĻāĻ•āϟāĻž āĻĒāϰāĻŋāĻˇā§āĻ•āĻžāϰ āϧāĻžāϰāĻŖāĻž āĻĨāĻžāĻ•āĻž āĻĻāϰāĻ•āĻžāϰ:

  • Multithreading → āĻāĻ•āχ āĻĒā§āϰāϏ⧇āϏ⧇āϰ āϭ⧇āϤāϰ⧇ āĻāĻ•āĻžāϧāĻŋāĻ• āĻĨā§āϰ⧇āĻĄ parallel āĻ āĻ•āĻžāϜ āĻ•āϰ⧇āĨ¤
  • Multiprocessing → āφāϞāĻžāĻĻāĻž āφāϞāĻžāĻĻāĻž āĻĒā§āϰāϏ⧇āϏ parallel āĻ āϚāϞ⧇, āĻĒā§āϰāĻ¤ā§āϝ⧇āĻ•āϟāĻž āĻĒā§āϰāϏ⧇āϏ⧇āϰ āύāĻŋāϜāĻ¸ā§āĻŦ memory āĻĨāĻžāϕ⧇āĨ¤

💡 āωāĻĻāĻžāĻšāϰāĻŖ:

  • Google Chrome āĻ āĻĒā§āϰāϤāĻŋāϟāĻž Tab āĻāĻ•āϟāĻž processāĨ¤
  • āĻ•āĻŋāĻ¨ā§āϤ⧁ āĻĒā§āϰāϤāĻŋāϟāĻž Tab āĻāϰ āϭ⧇āϤāϰ⧇ āφāĻŦāĻžāϰ āĻāĻ•āĻžāϧāĻŋāĻ• thread āϚāϞ⧇ (rendering, JS execution, network handling āχāĻ¤ā§āϝāĻžāĻĻāĻŋ)āĨ¤

Creating a Thread in C#

C# āĻ multithreading āĻļ⧁āϰ⧁ āĻ•āϰāĻžāϰ āϏāĻŦāĻšā§‡ā§Ÿā§‡ āϏāĻšāϜ āωāĻĒāĻžā§Ÿ āĻšāϞ⧋ Thread āĻ•ā§āϞāĻžāϏ āĻŦā§āϝāĻŦāĻšāĻžāϰ āĻ•āϰāĻžāĨ¤

1. Using the Thread Class

using System;
using System.Threading;

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

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

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

👉 āĻāĻ–āĻžāύ⧇ Main thread āφāϰ āύāϤ⧁āύ āϤ⧈āϰāĻŋ āĻ•āϰāĻž thread āĻāĻ•āχ āϏāĻžāĻĨ⧇ āĻ•āĻžāϜ āĻ•āϰāϛ⧇āĨ¤
Output interleaved āĻšāĻŦ⧇, āĻŽāĻžāύ⧇ āĻ•āĻ–āύ⧋ Main āφāϗ⧇ āφāϏāĻŦ⧇, āĻ•āĻ–āύ⧋ Thread āφāϗ⧇ āφāϏāĻŦ⧇āĨ¤

2. Thread with Lambda Expression

Thread t = new Thread(() =>
{
    Console.WriteLine("Thread started with Lambda!");
});
t.Start();

āĻāϟāĻž āϛ⧋āϟ āĻ•āĻžāĻœā§‡āϰ āϜāĻ¨ā§āϝ āϖ⧁āĻŦāχ handyāĨ¤

Thread Lifecycle

āĻāĻ•āϟāĻž thread āĻŦāĻŋāĻ­āĻŋāĻ¨ā§āύ state āĻāϰ āĻŽāĻ§ā§āϝ āĻĻāĻŋā§Ÿā§‡ āϝāĻžā§Ÿ:

  • Unstarted → Thread āϤ⧈āϰāĻŋ āĻšā§Ÿā§‡āϛ⧇ āĻ•āĻŋāĻ¨ā§āϤ⧁ āϚāĻžāϞ⧁ āĻšā§ŸāύāĻŋāĨ¤
  • Running → CPU āϤ⧇ āϚāϞāϛ⧇āĨ¤
  • Wait/Sleep/Join → āĻ•āĻŋāϛ⧁āĻ•ā§āώāĻŖ pause āφāϛ⧇āĨ¤
  • Stopped → āĻ•āĻžāϜ āĻļ⧇āώ āĻ•āϰ⧇ āĻŦ⧇āϰ āĻšā§Ÿā§‡ āϗ⧇āϛ⧇āĨ¤

(āĻāĻ–āĻžāύ⧇ āĻāĻ•āϟāĻž lifecycle diagram āĻŦā§āϝāĻŦāĻšāĻžāϰ āĻ•āϰāϞ⧇ āφāϰāĻ“ āĻĒāϰāĻŋāĻˇā§āĻ•āĻžāϰ āĻšāĻŦ⧇āĨ¤)

Common Pitfalls in Multithreading

Multithreading āĻļāĻ•ā§āϤāĻŋāĻļāĻžāϞ⧀ āĻšāϞ⧇āĻ“ āϭ⧁āϞ āĻ•āϰāϞ⧇ āĻŽāĻžāϰāĻžāĻ¤ā§āĻŽāĻ• bug āϤ⧈āϰāĻŋ āĻšāϤ⧇ āĻĒāĻžāϰ⧇āĨ¤

1. Race Condition

āϝāĻ–āύ āĻāĻ•āĻžāϧāĻŋāĻ• thread āĻāĻ•āχ data modify āĻ•āϰ⧇, āϤāĻ–āύ race condition āĻšā§ŸāĨ¤

class Counter
{
    public int Value = 0;

    public void Increment()
    {
        Value++; // Not thread-safe!
    }
}

👉 Value++ āĻāĻ•āϏāĻžāĻĨ⧇ ⧍āϟāĻž thread āĻ•āϰāϞ⧇ āĻĢāϞāĻžāĻĢāϞ āϗ⧁āϞāĻŋā§Ÿā§‡ āϝāĻžāĻŦ⧇āĨ¤

2. Deadlock

Deadlock āĻšāϞ⧋ āϝāĻ–āύ āĻĻ⧁āχ āĻŦāĻž āϤāĻžāϰ āĻŦ⧇āĻļāĻŋ thread āĻāϕ⧇ āĻ…āĻĒāϰ⧇āϰ lock āĻāϰ āϜāĻ¨ā§āϝ āĻ…āĻĒ⧇āĻ•ā§āώāĻž āĻ•āϰāϤ⧇ āĻĨāĻžāϕ⧇āĨ¤

object lock1 = new object();
object lock2 = new object();

Thread t1 = new Thread(() =>
{
    lock (lock1)
    {
        Thread.Sleep(100);
        lock (lock2) { Console.WriteLine("Thread 1 done"); }
    }
});

Thread t2 = new Thread(() =>
{
    lock (lock2)
    {
        Thread.Sleep(100);
        lock (lock1) { Console.WriteLine("Thread 2 done"); }
    }
});

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

👉 āĻāĻ–āĻžāύ⧇ t1 āĻāĻŦāĻ‚ t2 āĻāϕ⧇ āĻ…āĻĒāϰāϕ⧇ āϚāĻŋāϰāĻĻāĻŋāύ⧇āϰ āϜāĻ¨ā§āϝ wait āĻ•āϰāĻžāĻŦ⧇āĨ¤

Best Practices for Beginners

  • Directly Thread āĻ•ā§āϞāĻžāϏ āĻŦā§āϝāĻŦāĻšāĻžāϰ āύāĻž āĻ•āϰ⧇ āĻŦāϰāĻ‚ Task āĻŦāĻž ThreadPool āĻŦā§āϝāĻŦāĻšāĻžāϰ āĻ•āϰ⧁āύ (āĻāϗ⧁āϞ⧋ efficient)āĨ¤
  • āϏāĻŦāϏāĻŽā§Ÿ synchronization primitives (lock, Monitor, Mutex āχāĻ¤ā§āϝāĻžāĻĻāĻŋ) āĻŦā§āϝāĻŦāĻšāĻžāϰ āĻ•āϰ⧁āύāĨ¤
  • Deadlock āĻā§œāĻžāϤ⧇ consistent lock order follow āĻ•āϰ⧁āύāĨ¤
  • Multithreading debug āĻ•āϰāĻžāϰ āϏāĻŽā§Ÿ Visual Studio Diagnostic Tools āĻŦā§āϝāĻŦāĻšāĻžāϰ āĻ•āϰāϤ⧇ āĻĒāĻžāϰ⧇āύāĨ¤

Conclusion

Multithreading āĻšāϞ⧋ C# āĻāϰ āĻāĻ• āĻļāĻ•ā§āϤāĻŋāĻļāĻžāϞ⧀ āĻĢāĻŋāϚāĻžāϰ, āϝāĻž āφāĻĒāύāĻžāϕ⧇ parallel āĻāĻŦāĻ‚ responsive application āϤ⧈āϰāĻŋ āĻ•āϰāϤ⧇ āϏāĻžāĻšāĻžāĻ¯ā§āϝ āĻ•āϰ⧇āĨ¤ āϤāĻŦ⧇ āĻāϰ āϏāĻžāĻĨ⧇ āφāϏ⧇ āĻ•āĻŋāϛ⧁ challenge āϝ⧇āĻŽāύ race condition āĻāĻŦāĻ‚ deadlockāĨ¤

0 Comments