Complete C# Tutorial

C# Lock Example: Prevent Data Issues in Multithreading

Ever tried to use an ATM while someone else is also withdrawing from the same account? If the bank doesn’t handle this properly, both transactions might go through at the same time, leading to wrong balances. That’s exactly what happens in multithreading if we don’t lock shared data!

When multiple threads access the same data, they can mess it up. One thread might be updating a value while another is reading an old value, leading to unexpected results. The lock statement in C# helps prevent this mess by allowing only one thread at a time to access the critical section of code.

Sounds important, right? Let’s see how it works!

Why Do We Need Lock?

Here’s why the lock statement is super useful:

Prevents data corruption – Ensures only one thread modifies data at a time.
Avoids unexpected results – Multiple threads won’t overwrite each other’s work.
Makes multithreading safer – Reduces race conditions, which are tricky bugs to fix.
Improves stability – Your program runs smoothly even with multiple threads.

Real-World Scenario: Bank Account Balance Issue

Imagine an online banking app. Two people, Alex and Mia, try to withdraw money at the same time from the same account.

Without a lock, both withdrawals might happen simultaneously, leading to incorrect balances. However, with a lock, one transaction completes before the next one starts, preventing issues.

C# Lock Example: Fixing the Bank Account Issue

Now, let’s write some code to simulate this problem and fix it with a lock statement.

Without Lock (Problem Scenario)

				
					using System;
using System.Threading;

class BankAccount
{
    private int balance = 1000; // Initial balance

    public void Withdraw(int amount)
    {
        if (balance >= amount)
        {
            Console.WriteLine(Thread.CurrentThread.Name + " is withdrawing $" + amount);
            Thread.Sleep(100); // Simulating delay
            balance -= amount;
            Console.WriteLine(Thread.CurrentThread.Name + " completed withdrawal. Remaining balance: $" + balance);
        }
        else
        {
            Console.WriteLine(Thread.CurrentThread.Name + " tried to withdraw, but insufficient balance.");
        }
    }
}

class Program
{
    static void Main()
    {
        BankAccount account = new BankAccount();

        Thread t1 = new Thread(() => account.Withdraw(700)) { Name = "Alex" };
        Thread t2 = new Thread(() => account.Withdraw(700)) { Name = "Mia" };

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

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

Possible Output (Without Lock)

				
					Alex is withdrawing $700  
Mia is withdrawing $700  
Alex completed withdrawal. Remaining balance: $300  
Mia completed withdrawal. Remaining balance: -$400  ❌ (Incorrect!)
				
			

Problem Explanation:

  • Alex and Mia both check if there’s enough money at the same time.
  • Both see $1000 and decide to withdraw $700.
  • The bank should decline Mia’s request, but it doesn’t!
  • The balance goes negative, which is obviously wrong.

With Lock (Fixed Scenario)

				
					using System;
using System.Threading;

class BankAccount
{
    private int balance = 1000;
    private object lockObject = new object(); // Lock object

    public void Withdraw(int amount)
    {
        lock (lockObject) // Ensures only one thread can execute at a time
        {
            if (balance >= amount)
            {
                Console.WriteLine(Thread.CurrentThread.Name + " is withdrawing $" + amount);
                Thread.Sleep(100); // Simulating delay
                balance -= amount;
                Console.WriteLine(Thread.CurrentThread.Name + " completed withdrawal. Remaining balance: $" + balance);
            }
            else
            {
                Console.WriteLine(Thread.CurrentThread.Name + " tried to withdraw, but insufficient balance.");
            }
        }
    }
}

class Program
{
    static void Main()
    {
        BankAccount account = new BankAccount();

        Thread t1 = new Thread(() => account.Withdraw(700)) { Name = "Alex" };
        Thread t2 = new Thread(() => account.Withdraw(700)) { Name = "Mia" };

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

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

Correct Output (With Lock)

				
					Alex is withdrawing $700  
Alex completed withdrawal. Remaining balance: $300  
Mia tried to withdraw, but insufficient balance.
				
			

What Changed?

Only one thread enters the lock at a time.
✅ When Alex is withdrawing, Mia has to wait.
✅ By the time Mia checks the balance, it’s already $300, so the withdrawal is declined.
Problem solved! No incorrect balance.


When Should You Use Lock?

Use a lock statement when:
👉 Multiple threads access and modify the same data.
👉 You need to prevent race conditions (when two threads try to modify a value at the same time).
👉 You’re handling important data like bank transactions, inventory, or user accounts.
👉 You want your program to be thread-safe and bug-free.

Conclusion

Multithreading is awesome for making your program faster, but it can also lead to weird bugs if multiple threads mess with the same data.

Using the lock statement, you can prevent conflicts and ensure your program runs smoothly without data corruption. Now, go ahead and try it yourself! If you have difficulty or a question, drop a comment. We will be happy to help you. 😊

Next What?

In the next lesson, you’ll learn about the Using Statement—a super useful way to manage resources in C#. Stay tuned! 🚀

Leave a Comment

Share this Doc

Lock

Or copy link