Skip to Content
All posts

Optimistic vs. Pessimistic Locking in C#

Mastering Concurrency Control

2 min read ·  — #csharp-interview#senior#concurrency#optimistic-locking#pessimistic-locking

Understanding Concurrency in C#

Title: : Optimistic vs. Pessimistic Locking in C#

Introduction

Welcome to a deep dive into the realms of concurrency control in C#, a topic that sits at the heart of modern software development. In this post, we're going to unravel the intricacies of two pivotal concepts: Optimistic and Pessimistic Locking. These techniques are essential tools in a developer's toolkit, particularly when dealing with multi-threaded environments and database interactions where managing concurrent access is crucial.

Our journey will explore the theoretical underpinnings of these locking strategies, followed by practical examples and scenarios where each approach shines. This exploration is not just about understanding the 'how', but also the 'when' and 'why' of applying these locking mechanisms in real-world applications. Whether you're dealing with high-traffic web applications, complex transaction systems, or simply ensuring data integrity in multi-user environments, mastering these concepts is key to developing robust and efficient C# applications.


Optimistic Locking

Concept:

Optimistic Locking is based on the assumption that resource conflicts are rare, and it proceeds with transactions without locking resources. The validation occurs at the transaction's end, ensuring that no other transaction has modified the data being acted upon.

Real-World Scenario:

Imagine an online ticket booking system. Multiple users are trying to book the last few seats on a flight. With Optimistic Locking, each user proceeds to book without immediate checks. When they try to finalize their booking, the system checks if the seat is still available. If not, the user is informed that the booking could not be completed.

Example in C#:

public class TicketBooking
{
    public int SeatsAvailable { get; private set; }

    public bool TryBookSeat()
    {
        int initialSeats = SeatsAvailable;

        // Simulate booking process
        // ...

        // Check if the seat count hasn't changed during the booking process
        if (initialSeats == SeatsAvailable)
        {
            SeatsAvailable--; // Decrease seat count
            return true; // Booking successful
        }

        return false; // Booking failed, seat was taken
    }
}

Pessimistic Locking

Concept:

Pessimistic Locking takes a more cautious approach. It locks the resource at the start of the transaction, preventing other transactions from modifying it until the lock is released. This is common in scenarios where conflicts are frequent.

Real-World Scenario:

Consider a banking system where multiple transactions are being processed on the same account. Pessimistic Locking is used to lock the account record when a transaction starts, ensuring that the balance is accurately updated and consistent across transactions.

Example in C#:

public class BankAccount
{
    private object _lock = new object();
    public decimal Balance { get; private set; }

    public void Deposit(decimal amount)
    {
        lock (_lock)
        {
            // Lock acquired, safe to modify the balance
            Balance += amount;
        }
        // Lock released after exiting the block
    }

    public bool Withdraw(decimal amount)
    {
        lock (_lock)
        {
            if (Balance >= amount)
            {
                Balance -= amount;
                return true; // Withdrawal successful
            }

            return false; // Insufficient funds
        }
    }
}

Choosing Between Optimistic and Pessimistic Locking

The choice between these two strategies depends on the nature of your application:

  • Use Optimistic Locking when you expect low contention for resources. It's less resource-intensive and can offer better performance in scenarios where conflicts are rare.

  • Use Pessimistic Locking when dealing with high-conflict scenarios or when maintaining strict data integrity is crucial, as in financial applications.

In conclusion, understanding and choosing the right locking mechanism is crucial for developing efficient and reliable C# applications. By carefully considering your application's needs and the likelihood of resource contention, you can select the most appropriate strategy to ensure data integrity and optimal performance.