Producer Consumer Solution using Semaphores (original) (raw)

Last Updated : 3 Sep, 2025

The Producer-Consumer problem is a classic example of a synchronization problem in operating systems. It demonstrates how processes or threads can safely share resources without conflicts. This problem belongs to the process synchronization domain, specifically dealing with coordination between multiple processes sharing a common buffer.

In this problem, we have:

The main challenge is to ensure:

  1. A producer does not add data to a full buffer.
  2. A consumer does not remove data from an empty buffer.
  3. Multiple producers and consumers do not access the buffer simultaneously, preventing race conditions.

A semaphore is an integer-based signaling mechanism used to coordinate access to shared resources. It supports two atomic operations:

wait(S){

while(S <= 0); // busy waiting
S--;

}

signal(S){
S++;

}

Problem Statement

Consider a fixed-size buffer shared between a producer and a consumer.

The buffer is the critical section. At any moment:

To manage this, we use three semaphores:

Semaphore Initialization

mutex = 1; // binary semaphore for mutual exclusion
full = 0; // initially no filled slots
empty = n; // buffer size

Producer

do {

// Produce an item
wait(empty); // Check for empty slot
wait(mutex); // Enter critical section

// Place item in buffer

signal(mutex); // Exit critical section
signal(full); // Increase number of full slots
} while (true);

Consumer

do {
wait(full); // Check for filled slot
wait(mutex); // Enter critical section

// Remove item from buffer

signal(mutex); // Exit critical section
signal(empty); // Increase number of empty slots
} while (true);

**Explanation:

**Code Example Using POSIX Semaphores

C++ `

#include #include #include #include #include #include

#define BUFFER_SIZE 5

int buffer[BUFFER_SIZE]; int in = 0, out = 0, count = 0;

std::mutex mutex; std::condition_variable not_full; std::condition_variable not_empty;

// Producer function void producer() { while (true) { int item = rand() % 100; // produce an item

    std::unique_lock<std::mutex> lock(mutex);

    while (count == BUFFER_SIZE) // buffer full
        not_full.wait(lock);

    buffer[in] = item;
    std::cout << "Produced: " << item << " at " << in << std::endl;
    in = (in + 1) % BUFFER_SIZE;
    count++;

    not_empty.notify_one(); // signal buffer has item

    lock.unlock();

    std::this_thread::sleep_for(std::chrono::seconds(1)); // simulate production time
}

}

// Consumer function void consumer() { while (true) { std::unique_lockstd::mutex lock(mutex);

    while (count == 0) // buffer empty
        not_empty.wait(lock);

    int item = buffer[out];
    std::cout << "Consumed: " << item << " at " << out << std::endl;
    out = (out + 1) % BUFFER_SIZE;
    count--;

    not_full.notify_one(); // signal buffer has space

    lock.unlock();

    std::this_thread::sleep_for(std::chrono::seconds(1)); // simulate consumption time
}

}

int main() { std::srand(std::time(0)); std::thread prod(producer); std::thread cons(consumer);

prod.join();
cons.join();

return 0;

}

C

#include <stdio.h> #include <pthread.h> #include <stdlib.h> #include <unistd.h>

#define BUFFER_SIZE 5

int buffer[BUFFER_SIZE]; int in = 0, out = 0, count = 0;

pthread_mutex_t mutex; pthread_cond_t not_full; pthread_cond_t not_empty;

// Producer function void* producer(void* arg) { while (1) { int item = rand() % 100; // produce an item

    pthread_mutex_lock(&mutex);

    while (count == BUFFER_SIZE) // buffer full
        pthread_cond_wait(&not_full, &mutex);

    buffer[in] = item;
    printf("Produced: %d at %d\n", item, in);
    in = (in + 1) % BUFFER_SIZE;
    count++;

    pthread_cond_signal(&not_empty); // signal buffer has item
    pthread_mutex_unlock(&mutex);

    sleep(1); // simulate production time
}
return NULL;

}

// Consumer function void* consumer(void* arg) { while (1) { pthread_mutex_lock(&mutex);

    while (count == 0) // buffer empty
        pthread_cond_wait(&not_empty, &mutex);

    int item = buffer[out];
    printf("Consumed: %d at %d\n", item, out);
    out = (out + 1) % BUFFER_SIZE;
    count--;

    pthread_cond_signal(&not_full); // signal buffer has space
    pthread_mutex_unlock(&mutex);

    sleep(1); // simulate consumption time
}
return NULL;

}

int main() { pthread_t prod, cons;

pthread_mutex_init(&mutex, NULL);
pthread_cond_init(&not_full, NULL);
pthread_cond_init(&not_empty, NULL);

pthread_create(&prod, NULL, producer, NULL);
pthread_create(&cons, NULL, consumer, NULL);

pthread_join(prod, NULL);
pthread_join(cons, NULL);

pthread_mutex_destroy(&mutex);
pthread_cond_destroy(&not_full);
pthread_cond_destroy(&not_empty);

return 0;

}

Java

import java.util.concurrent.locks.Condition; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock;

public class ProducerConsumer { private static final int BUFFER_SIZE = 5; private static final int[] buffer = new int[BUFFER_SIZE]; private static int in = 0, out = 0, count = 0; private static final Lock lock = new ReentrantLock(); private static final Condition notFull = lock.newCondition(); private static final Condition notEmpty = lock.newCondition();

public static void main(String[] args) {
    Thread producer = new Thread(ProducerConsumer::producer);
    Thread consumer = new Thread(ProducerConsumer::consumer);

    producer.start();
    consumer.start();
}

private static void producer() {
    while (true) {
        int item = (int) (Math.random() * 100); // produce an item

        lock.lock();
        try {
            while (count == BUFFER_SIZE) // buffer full
                notFull.await();

            buffer[in] = item;
            System.out.println("Produced: " + item + " at " + in);
            in = (in + 1) % BUFFER_SIZE;
            count++;

            notEmpty.signal(); // signal buffer has item
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }

        try {
            Thread.sleep(1000); // simulate production time
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

private static void consumer() {
    while (true) {
        lock.lock();
        try {
            while (count == 0) // buffer empty
                notEmpty.await();

            int item = buffer[out];
            System.out.println("Consumed: " + item + " at " + out);
            out = (out + 1) % BUFFER_SIZE;
            count--;

            notFull.signal(); // signal buffer has space
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }

        try {
            Thread.sleep(1000); // simulate consumption time
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

}

Python

import threading import random import time

BUFFER_SIZE = 5

buffer = [0] * BUFFER_SIZE in_index = out_index = count = 0

mutex = threading.Lock() not_full = threading.Condition(mutex) not_empty = threading.Condition(mutex)

Producer function

def producer(): global in_index, out_index, count while True: item = random.randint(0, 99) # produce an item

    with not_full:
        while count == BUFFER_SIZE:  # buffer full
            not_full.wait()

        buffer[in_index] = item
        print(f'Produced: {item} at {in_index}')
        in_index = (in_index + 1) % BUFFER_SIZE
        count += 1

        not_empty.notify()  # signal buffer has item

    time.sleep(1)  # simulate production time

Consumer function

def consumer(): global in_index, out_index, count while True: with not_empty: while count == 0: # buffer empty not_empty.wait()

        item = buffer[out_index]
        print(f'Consumed: {item} at {out_index}')
        out_index = (out_index + 1) % BUFFER_SIZE
        count -= 1

        not_full.notify()  # signal buffer has space

    time.sleep(1)  # simulate consumption time

if name == 'main': prod_thread = threading.Thread(target=producer) cons_thread = threading.Thread(target=consumer)

prod_thread.start()
cons_thread.start()

prod_thread.join()
cons_thread.join()

`

The Producer-Consumer problem is a fundamental example of process synchronization in operating systems. Using semaphores and mutexes, we can ensure:

This approach prevents race conditions, data inconsistency, and deadlocks, making it a reliable solution for coordinating multiple processes.