Complete C# Tutorial

Deadlocks and Common Pitfalls in Thread – Avoid Freezing Code

Hey, multithreading champ! πŸ‘‹ Have you ever faced a situation where your program just freezes? Everything seems fine, but nothing moves?

Yep, you just ran into a deadlock! 😱

But don’t worry! In this lesson, we’ll talk about Deadlocks and common pitfalls in Thread, how they happen, and how to avoid them like a pro! πŸš€

πŸ›‘ What is a Deadlock?

A deadlock happens when two or more threads are waiting for each other to release a resource, but neither releases it. 😡

🎭 Imagine This…

  • You and your friend are in a hallway.
  • You both want to pass, but you block each other.
  • You say, “You go first!” and your friend says, “No, you go first!”

❌ Boom! No one moves. Deadlock! 🀯

The same thing happens in multithreading when two threads wait for each other forever!

⚑ Example 1: A Simple Deadlock in Threads

Let’s see a deadlock in action!

Β 

βœ… Code Example

				
					using System;
using System.Threading;

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

    static void Thread1()
    {
        lock (lock1)
        {
            Console.WriteLine("Thread 1: Locked lock1");
            Thread.Sleep(1000);
            
            lock (lock2)  // Waiting for lock2
            {
                Console.WriteLine("Thread 1: Locked lock2");
            }
        }
    }

    static void Thread2()
    {
        lock (lock2)
        {
            Console.WriteLine("Thread 2: Locked lock2");
            Thread.Sleep(1000);
            
            lock (lock1)  // Waiting for lock1
            {
                Console.WriteLine("Thread 2: Locked lock1");
            }
        }
    }

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

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

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

        Console.WriteLine("Program completed");
    }
}
				
			

❌ Expected Output (Program Freezes)

				
					Thread 1: Locked lock1
Thread 2: Locked lock2
				
			

πŸ’₯ Oops! Both threads are waiting for each other forever. Deadlock!

πŸ›‘ Common Pitfalls in Threading

⚠️ Pitfall 1: Forgetting to Release a Lock

If you lock something and never release it, your program may freeze.

Β 

βœ… How to Fix It? Use Monitor with try/finally.

				
					Monitor.Enter(lock1);
try
{
    // Critical Section
}
finally
{
    Monitor.Exit(lock1);  // Always release the lock!
}
				
			

⚠️ Pitfall 2: Using Too Many Locks

Too many locks increase the chance of deadlocks. Keep your locking minimal!

Β 

βœ… How to Fix It? Always lock in the same order.

βœ… If Thread A locks Resource1 first, then Resource2,
βœ… Thread B should follow the same order!

				
					lock (lock1)
{
    lock (lock2)
    {
        // Code
    }
}
				
			

βœ… No deadlocks! πŸŽ‰

⚠️ Pitfall 3: Using Locks Unnecessarily

Don’t lock everything! If multiple threads only read data, no need to lock!

Β 

βœ… How to Fix It? Use ReaderWriterLockSlim instead of lock.

				
					ReaderWriterLockSlim rwLock = new ReaderWriterLockSlim();
rwLock.EnterReadLock();
// Read operation
rwLock.ExitReadLock();
				
			

πŸ† Example 2: Fixing Deadlocks with TryLock

One way to avoid deadlocks is to use Monitor.TryEnter instead of lock.

Β 

βœ… Code Example

				
					using System;
using System.Threading;

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

    static void Thread1()
    {
        if (Monitor.TryEnter(lock1, 1000))  
        {
            Console.WriteLine("Thread 1: Locked lock1");
            Thread.Sleep(500);

            if (Monitor.TryEnter(lock2, 1000))
            {
                Console.WriteLine("Thread 1: Locked lock2");
                Monitor.Exit(lock2);
            }
            Monitor.Exit(lock1);
        }
        else
        {
            Console.WriteLine("Thread 1: Could not acquire lock1");
        }
    }

    static void Thread2()
    {
        if (Monitor.TryEnter(lock2, 1000))
        {
            Console.WriteLine("Thread 2: Locked lock2");
            Thread.Sleep(500);

            if (Monitor.TryEnter(lock1, 1000))
            {
                Console.WriteLine("Thread 2: Locked lock1");
                Monitor.Exit(lock1);
            }
            Monitor.Exit(lock2);
        }
        else
        {
            Console.WriteLine("Thread 2: Could not acquire lock2");
        }
    }

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

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

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

        Console.WriteLine("Program completed");
    }
}
				
			

βœ… Expected Output

				
					Thread 1: Locked lock1
Thread 2: Locked lock2
Thread 1: Could not acquire lock2
Thread 2: Could not acquire lock1
Program completed
				
			

βœ… No deadlocks! If a lock is unavailable, the thread moves on instead of waiting forever.

🌍 Real-World Example: Two Database Transactions Deadlocking

1️⃣ You have two bank transactions:

  1. Transaction A locks Account1, then Account2.
  2. Transaction B locks Account2, then Account1.

2️⃣ Both wait forever for the other to release the lock. Deadlock!

πŸ’‘ Fix: Always lock resources in the same order to prevent deadlocks!

πŸ“Œ Key Takeaways

  1. Deadlocks and common pitfalls in Thread can freeze your program.
  2. Deadlocks happen when threads wait for each other forever.
  3. Always lock in the same order to avoid deadlocks.
  4. Use Monitor.TryEnter() to prevent infinite waiting.
  5. Avoid unnecessary locks for better performance.

Β 

πŸš€ Next What?

πŸŽ‰ You did it! Now you know how to avoid deadlocks and threading pitfalls!

But wait… what if you need to manage thousands of threads efficiently? 🀯

πŸ”₯ Next up: Thread Pooling in C# – Boost Performance Like a Pro! Stay tuned! πŸš€

Leave a Comment

Share this Doc

Deadlocks and common pitfalls

Or copy link