In this tutorial, we will cover how to use semaphores in Python. A semaphore is a synchronization object that is used to control access to a common resource by multiple threads in a concurrent or multi-threaded environment, such as Python.
Semaphores are useful in situations where you need to limit the number of threads that can access a resource at a time. Python’s standard library provides the Semaphore class in the threading module, which makes implementing semaphores easy and efficient.
In this tutorial, we will demonstrate how to create and use a semaphore in Python with a step-by-step example.
Step 1: Import the threading module
First, we need to import the threading module to gain access to the Semaphore class and other useful threading-related classes and functions.
1 |
import threading |
Step 2: Create a Semaphore instance
Create an instance of the Semaphore class, specifying the maximum number of threads that can access the shared resource concurrently. If you don’t provide a value, the default is 1 which means the semaphore will act as a simple lock.
1 2 |
num_allowed_threads = 3 semaphore = threading.Semaphore(num_allowed_threads) |
Step 3: Create a function to access the shared resource
Now, we will create a function that represents the shared resource being accessed by multiple threads, wrapped by the semaphore to limit concurrency.
1 2 3 4 5 6 7 |
import time def limited_function(thread_id): with semaphore: print(f"Thread {thread_id} is accessing the shared resource") time.sleep(2) print(f"Thread {thread_id} has finished using the shared resource") |
Note: The above code uses the with statement to acquire and release the semaphore automatically. This is a recommended approach as it ensures that the semaphore is properly released even if an exception occurs.
Step 4: Spawn multiple threads to run the function concurrently
Now we will create multiple threads that will concurrently run the limited_function function, showing how the semaphore manages access to the shared resource.
1 2 3 4 5 6 7 8 9 10 11 |
NUM_THREADS = 6 threads = [] for i in range(NUM_THREADS): thread = threading.Thread(target=limited_function, args=(i,)) threads.append(thread) thread.start() for thread in threads: thread.join() |
Full code:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
import threading import time num_allowed_threads = 3 semaphore = threading.Semaphore(num_allowed_threads) def limited_function(thread_id): with semaphore: print(f"Thread {thread_id} is accessing the shared resource") time.sleep(2) print(f"Thread {thread_id} has finished using the shared resource") NUM_THREADS = 6 threads = [] for i in range(NUM_THREADS): thread = threading.Thread(target=limited_function, args=(i,)) threads.append(thread) thread.start() for thread in threads: thread.join() |
Output:
Thread 0 is accessing the shared resource Thread 1 is accessing the shared resource Thread 2 is accessing the shared resource Thread 0 has finished using the shared resource Thread 3 is accessing the shared resource Thread 1 has finished using the shared resource Thread 4 is accessing the shared resource Thread 2 has finished using the shared resource Thread 5 is accessing the shared resource Thread 3 has finished using the shared resource Thread 4 has finished using the shared resource Thread 5 has finished using the shared resource
Conclusion
In this tutorial, we learned how to use the semaphore class in Python to control access to a shared resource in a multi-threaded environment. Semaphores are an essential tool for synchronizing and managing concurrent access to resources, helping to ensure data integrity and prevent race conditions. By following the steps outlined above, you can easily implement semaphores in your Python programs.