0
0
CsharpHow-ToBeginner · 3 min read

How to Use lock in C#: Simple Thread Safety Guide

In C#, use the lock statement to ensure that only one thread can access a block of code at a time, preventing race conditions. You provide an object as a lock token, and the code inside the lock block runs exclusively for one thread until it finishes.
📐

Syntax

The lock statement requires an object to lock on, called the lock token. The syntax is:

lock (lockObject)
{
    // code to protect
}

Here, lockObject is any reference type object used to control access. The code inside the braces runs exclusively by one thread at a time.

csharp
lock (lockObject)
{
    // critical section code
}
💻

Example

This example shows two threads incrementing a shared counter safely using lock. Without lock, the final count may be incorrect due to race conditions.

csharp
using System;
using System.Threading;

class Program
{
    private static int counter = 0;
    private static readonly object lockObj = new object();

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

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

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

        Console.WriteLine($"Final counter value: {counter}");
    }

    static void IncrementCounter()
    {
        for (int i = 0; i < 100000; i++)
        {
            lock (lockObj)
            {
                counter++;
            }
        }
    }
}
Output
Final counter value: 200000
⚠️

Common Pitfalls

  • Locking on public or mutable objects: This can cause deadlocks or unexpected behavior. Always lock on a private, readonly object.
  • Locking on this or string literals: Avoid locking on this or strings because other code might lock on the same object, causing conflicts.
  • Long-running code inside lock: Keep the locked code short to avoid blocking other threads for too long.
  • Forgetting to lock shared data: Leads to race conditions and unpredictable results.
csharp
/* Wrong way: locking on public object */
public class Example
{
    public object LockObj = new object();

    public void DoWork()
    {
        lock (LockObj) // risky if LockObj is accessible outside
        {
            // critical code
        }
    }
}

/* Right way: private readonly lock object */
public class SafeExample
{
    private readonly object _lock = new object();

    public void DoWork()
    {
        lock (_lock)
        {
            // critical code
        }
    }
}
📊

Quick Reference

  • Use a private readonly object as lock token.
  • Keep locked code short and fast.
  • Never lock on this or strings.
  • Use lock to prevent race conditions in multithreaded code.

Key Takeaways

Use the lock statement with a private readonly object to protect shared code from concurrent access.
Keep the code inside the lock block short to avoid blocking other threads.
Never lock on public objects, strings, or the current instance (this) to prevent deadlocks.
Locking ensures only one thread runs the critical section at a time, preventing race conditions.