A race condition occurs in programming when multiple threads access a shared resource concurrently and the final result depends on the unpredictable order of their execution. This can lead to inconsistent and non-deterministic program behavior.
1. Race condition issue in concurrent programming
A race condition occurs in programming when multiple threads access a shared resource concurrently and the final result depends on the unpredictable order of their execution. This can lead to inconsistent and non-deterministic program behavior.
Here's an example: You have 10,000 credits in your account and you want to purchase an item worth 5,000 credits. Normally, you can only buy a maximum of 2 products.

However, if you log into your account on 5 devices and make purchases simultaneously, you could potentially buy 5 products for only 5,000 credits. This is because the parallel threads have all checked if there are sufficient funds to make the payment at the same time.

This severe vulnerability enables users to exploit the system and make multiple purchases for the price of one. Although there are various approaches to mitigating this issue, I propose a straightforward solution: implementing a locking mechanism.
2. Locking mechanism
A locking mechanism is a synchronization tool that ensures data consistency in multi-threaded functions. In other words, a lock guarantees that one or more threads can read and modify a shared resource while it holds the lock.

The following are several types of locks:
- Mutex lock (Mutual Exclusion) : A mutex acts as a gatekeeper, ensuring that only a single thread can access a shared resource at any given moment.
- Semaphore lock : A semaphore is a signaling mechanism that controls the number of concurrent processes or threads accessing a shared resource.
- Readers/writer lock : A readers-writer lock provides a mechanism for controlling concurrent access to a shared resource, allowing multiple readers but only one writer at a time.
Each type of lock operates differently, so we must flexibly use each type of lock that is suitable for each case.
Returning to our initial example when starting the order process, I set a mutex lock to ensure that only one thread is allowed to read or modify the balance at a time.

If multiple orders are placed simultaneously, subsequent requests have to wait until they acquire the lock before they can proceed. As a result, I was only able to purchase 2 products and 3 other requests were rejected.

3. Problems of locking mechanisms
Although locks are crucial for preserving data integrity in multithreaded environments, developers must exercise caution when using them to avoid potential pitfalls.
- Decrease processing speed : Multithreading is implemented to maximize hardware utilization by processing multiple tasks concurrently. However, excessive use of locks can lead to significant performance degradation as threads are forced to wait for locks, hindering overall processing speed.

- Deadlock: A deadlock occurs when two or more threads are blocked, each waiting for a lock held by one of the others. This results in none of the threads being able to proceed, or a thread is waiting for a lock from a thread that has encountered an error and cannot release the lock.
Example : Suppose there are two processes, A and B, both of which need to access two resources, R1 and R2. If A holds R1 and is waiting for R2, while B holds R2 and is waiting for R1, then both processes will be blocked, waiting for each other, and neither can release its resource.
4. Principles of using locks
- Clearly define the scope of lock usage : locks should be placed at the read and write operations of shared resources. Avoid placing locks on long-running processes. Only place locks where necessary.
- Choose the most suitable lock type for the specific task.
- Implement a timeout mechanism to prevent indefinite waiting : Even if an operation cannot be completed due to waiting for a lock, we should set a timeout to notify the user of the failure, as they won't know what's happening inside the software. Waiting too long can also lead to device freezing.
- Ensure that locks are always released, even in error conditions.
Illustration image from freepik.com
Cover image from freepik.com
Reference
1 . The lock statement - ensure exclusive access to a shared resource