The threading.Semaphore class in Python’s threading module provides a way to manage a counter that is decremented each time a thread acquires the semaphore and incremented each time a thread releases it. This is useful for controlling access to a resource that has a limited capacity.
Table of Contents
- Introduction
threading.SemaphoreClass Syntax- Examples
- Basic Usage
- Using Semaphore to Control Access to a Resource
- Using BoundedSemaphore
- Real-World Use Case
- Conclusion
Introduction
The threading.Semaphore class is used to manage access to a resource by maintaining a counter. The counter is decremented when a thread acquires the semaphore and incremented when a thread releases it. When the counter reaches zero, any additional threads attempting to acquire the semaphore will block until the counter is incremented again.
threading.Semaphore Class Syntax
Here is how you create and use a semaphore with the threading.Semaphore class:
import threading
semaphore = threading.Semaphore(value=1)
Parameters:
value: The initial value of the semaphore counter. The default is 1.
Methods:
acquire(blocking=True, timeout=None): Decrement the counter and block if the counter is zero. IfblockingisTrue(the default), the method will block until the counter is incremented or the optional timeout occurs. IfblockingisFalse, the method will return immediately withTrueif the semaphore is acquired andFalseotherwise.release(): Increment the counter and wake up any threads blocked onacquire().
Examples
Basic Usage
Create and use a semaphore to synchronize threads.
Example
import threading
import time
semaphore = threading.Semaphore(2)
def worker(worker_id):
print(f"Worker {worker_id} waiting to acquire semaphore")
with semaphore:
print(f"Worker {worker_id} acquired semaphore")
time.sleep(2)
print(f"Worker {worker_id} releasing semaphore")
threads = [threading.Thread(target=worker, args=(i,)) for i in range(4)]
for thread in threads:
thread.start()
for thread in threads:
thread.join()
Using Semaphore to Control Access to a Resource
Use a semaphore to limit access to a resource, such as a database connection pool.
Example
import threading
import time
semaphore = threading.Semaphore(3)
def access_resource(thread_id):
with semaphore:
print(f"Thread {thread_id} accessing resource")
time.sleep(1)
print(f"Thread {thread_id} releasing resource")
threads = [threading.Thread(target=access_resource, args=(i,)) for i in range(5)]
for thread in threads:
thread.start()
for thread in threads:
thread.join()
Using BoundedSemaphore
Use a BoundedSemaphore to prevent the semaphore counter from exceeding its initial value.
Example
import threading
import time
semaphore = threading.BoundedSemaphore(2)
def worker(worker_id):
print(f"Worker {worker_id} waiting to acquire semaphore")
with semaphore:
print(f"Worker {worker_id} acquired semaphore")
time.sleep(2)
print(f"Worker {worker_id} releasing semaphore")
threads = [threading.Thread(target=worker, args=(i,)) for i in range(4)]
for thread in threads:
thread.start()
for thread in threads:
thread.join()
Real-World Use Case
Controlling Access to a Shared Resource
Use a semaphore to control access to a shared resource, such as a printing queue or a limited number of network connections.
Example
import threading
import time
semaphore = threading.Semaphore(3)
def print_job(job_id):
with semaphore:
print(f"Job {job_id} is being processed")
time.sleep(1)
print(f"Job {job_id} is done")
jobs = [threading.Thread(target=print_job, args=(i,)) for i in range(10)]
for job in jobs:
job.start()
for job in jobs:
job.join()
Conclusion
The threading.Semaphore class is used for managing access to shared resources in multithreaded programs. It allows you to control the number of threads that can access a resource simultaneously, ensuring that the resource is not overwhelmed. Proper usage can significantly enhance the reliability and efficiency of your concurrent applications.