Producer-consumer problem with semaphores

The producer-consumer problem is a classic synchronization problem in computer science, where there are two types of threads: producers and consumers. Producers generate data items and put them into a buffer, while consumers retrieve and process these data items from the buffer.

Problem statement

We have a shared buffer that is used to transfer data items between multiple producer threads and multiple consumer threads. The buffer has a maximum size, and producers should not overflow the buffer by adding data items when it is full. Similarly, consumers should not attempt to retrieve data items from an empty buffer.

Our task is to design a solution that allows producers and consumers to work concurrently and efficiently while ensuring synchronization and proper access to the shared buffer.

What are semaphores?

semaphore is a synchronization primitive that controls access to a shared resource. It typically has a count value and supports two main operations: wait (P) and signal (V). The wait operation decreases the count, and if the count becomes negative, it blocks the thread until it becomes positive again. The signal operation increases the count, potentially unblocking a waiting thread.

Using semaphores to solve the problem

To solve the producer-consumer problem using semaphores, we can follow these steps:

  1. Initialize the semaphores

  • Create a semaphore named empty and initialize its count to the maximum buffer size (representing the number of empty slots in the buffer).

  • Create a semaphore named full and initialize its count to 0 (representing the number of filled slots in the buffer).

  • Create a mutex semaphore named mutex and initialize its count to 1 (used to protect the critical sections of code).

from threading import Thread, Semaphore
import time
buffer = [] # Shared buffer
buffer_size = 5 # Maximum buffer size
# Initialize semaphores
empty = Semaphore(buffer_size) # Number of empty slots in the buffer
full = Semaphore(0) # Number of filled slots in the buffer
mutex = Semaphore(1) # Mutex to protect critical sections
  1. Implement the producer code

  • Generate a data item.

  • Acquire the semaphore empty to check if there is an available empty slot in the buffer. If the count is 0, the thread blocks until a slot becomes available.

  • Acquire the semaphore mutex to protect the critical section.

  • Add the data item to the buffer.

  • Release the semaphore mutex to allow other threads to access the critical section.

  • Signal the semaphore full to indicate that a new data item is available in the buffer.

class Producer(Thread):
def run(self):
while True:
data_item = generate_data_item()
empty.acquire() # Check if there's an available empty slot
mutex.acquire() # Protect the critical section
buffer.append(data_item) # Add the data item to the buffer
mutex.release()
full.release() # Signal that a new data item is available
time.sleep(1) # Simulate some time for data generation
  1. Implement the consumer code

  • Acquire the semaphore full to check if there is a data item available in the buffer. If the count is 0, the thread blocks until a data item is produced.

  • Acquire the semaphore mutex to protect the critical section.

  • Retrieve a data item from the buffer.

  • Release the semaphore mutex to allow other threads to access the critical section.

  • Signal the semaphore empty to indicate that an empty slot is available in the buffer.

  • Process the retrieved data item.

class Consumer(Thread):
def run(self):
while True:
full.acquire() # Check if there's a data item available
mutex.acquire() # Protect the critical section
data_item = buffer.pop(0) # Retrieve a data item from the buffer
mutex.release()
empty.release() # Signal that an empty slot is available
process_data_item(data_item) # Process the retrieved data item
time.sleep(1) # Simulate some time for data processing

Conclusion

Using semaphores provides a structured and reliable way to solve the producer-consumer problem, enabling concurrent execution of producers and consumers while maintaining synchronization and integrity of shared resources.

Free Resources

Copyright ©2025 Educative, Inc. All rights reserved