As we saw ๐๏ธ๐๏ธ in our previous blogs, threading is susceptible to race conditions. To tackle race conditions, we may use locks and semaphores to manage synchronization and data consistency while using threads
There are 3 major techniques for thread synchronization
- Lock ๐
- Rlock ยฎ๏ธ๐
- Semaphore, BoundedSemaphore
Lock
- A Lock is a basic synchronization primitivethat provides exclusive access to a shared resource.
- It has two states: lockedandunlocked.
- Only one thread can acquire the lock at a time. If another thread tries to acquire a locked lock, it will be blocked until the lock is released.
- It is not reentrant, meaning that a thread holding the lock cannot acquire it again without releasing it first.
withcontext manager, manages acquiring and releasing lock for us, other wise we can use lock.acquire() and lock.release() methods as well
from threading import Lock
lock = Lock()
def example_function():
    with lock:
        # critical section
        pass
RLock (Reentrant Lock):
- An RLock is an extension of the basic Lock that allows a thread to acquire the lock multiple times(reentrant).
- A thread that already holds the lock can acquire it again without blocking, but it must release the lock the same number of times it acquired it.
- Useful in situations where a function might need to acquire the same lock recursively.
from threading import RLock
rlock = RLock()
def example_function():
    with rlock:
        # critical section
        pass
Semaphore
- A Semaphore is a more generalized synchronization primitivethat allows a specified number of threads to access a shared resource concurrently.
- It maintains a counter that is decremented each time a thread acquires it and incremented when the thread releases it.
- Useful for controlling access to a resource with limited capacity.
from threading import Semaphore
semaphore = Semaphore(3)  # Allow up to 3 threads to access the resource concurrently
def example_function():
    with semaphore:
        # critical section
        pass
BoundedSemaphore
- A BoundedSemaphore is similar to Semaphore but has an upper limiton its counter.
- It raises a ValueError if the counter exceeds the specified bound when incremented.
from threading import BoundedSemaphore
bounded_semaphore = BoundedSemaphore(3)  # Bound the semaphore to allow up to 3 threads
def example_function():
    with bounded_semaphore:
        # critical section
        pass
conclusion
- In summary, Lock and RLock provide exclusive access to a shared resource, while Semaphore and BoundedSemaphore allow a specified number of threads to access a shared resource concurrently. The choice between them depends on the synchronization requirements of your multithreaded application
 
 
              
 
    
Top comments (2)
Very clear explanation & comparison of somewhat easily confusable concepts!
thanks for appreciating ๐ซ