Producer-Consumer Problem in C

The producer-consumer problem is a classic synchronization challenge in concurrent programming where multiple threads share a common buffer. Producers generate data and place it into the buffer, while consumers remove and process data from the buffer. The main challenge is coordinating these operations to prevent race conditions and ensure data integrity.

Syntax

/* Basic structure for producer-consumer implementation */
pthread_t producer_thread, consumer_thread;
pthread_mutex_t mutex;
pthread_cond_t condition_variable;

void* producer(void* arg);
void* consumer(void* arg);

Problem Statement

The producer-consumer problem involves two types of processes

  • Producer Generates data and puts it into a shared buffer
  • Consumer Takes data from the buffer and processes it

Key challenges include preventing buffer overflow, buffer underflow, and ensuring mutual exclusion when accessing the shared buffer.

Synchronization Requirements

To solve this problem effectively, we need

  • Mutual Exclusion Only one thread can access the buffer at a time
  • Buffer Full Condition Producer must wait when buffer is full
  • Buffer Empty Condition Consumer must wait when buffer is empty

Method 1: Using Mutex and Condition Variables

This approach uses pthread mutexes for mutual exclusion and condition variables for thread coordination

Note: To compile this program, use: gcc -o producer_consumer program.c -lpthread

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

#define BUFFER_SIZE 5
#define MAX_ITEMS 10

int buffer[BUFFER_SIZE];
int in = 0;
int out = 0;
int count = 0;
int produced_count = 0;
int consumed_count = 0;

pthread_mutex_t mutex;
pthread_cond_t full;
pthread_cond_t empty;

void* producer(void* arg) {
    int item = 1;
    
    while (produced_count < MAX_ITEMS) {
        pthread_mutex_lock(&mutex);
        
        while (count == BUFFER_SIZE) {
            pthread_cond_wait(&empty, &mutex);
        }
        
        buffer[in] = item;
        printf("Produced: %d<br>", item);
        in = (in + 1) % BUFFER_SIZE;
        count++;
        item++;
        produced_count++;
        
        pthread_cond_signal(&full);
        pthread_mutex_unlock(&mutex);
        
        usleep(100000); /* 0.1 second delay */
    }
    
    return NULL;
}

void* consumer(void* arg) {
    while (consumed_count < MAX_ITEMS) {
        pthread_mutex_lock(&mutex);
        
        while (count == 0) {
            pthread_cond_wait(&full, &mutex);
        }
        
        int item = buffer[out];
        printf("Consumed: %d<br>", item);
        out = (out + 1) % BUFFER_SIZE;
        count--;
        consumed_count++;
        
        pthread_cond_signal(&empty);
        pthread_mutex_unlock(&mutex);
        
        usleep(150000); /* 0.15 second delay */
    }
    
    return NULL;
}

int main() {
    pthread_t producer_thread, consumer_thread;
    
    pthread_mutex_init(&mutex, NULL);
    pthread_cond_init(&full, NULL);
    pthread_cond_init(&empty, NULL);
    
    pthread_create(&producer_thread, NULL, producer, NULL);
    pthread_create(&consumer_thread, NULL, consumer, NULL);
    
    pthread_join(producer_thread, NULL);
    pthread_join(consumer_thread, NULL);
    
    pthread_mutex_destroy(&mutex);
    pthread_cond_destroy(&full);
    pthread_cond_destroy(&empty);
    
    printf("Producer-Consumer execution completed.<br>");
    return 0;
}
Produced: 1
Produced: 2
Consumed: 1
Produced: 3
Consumed: 2
Produced: 4
Consumed: 3
Produced: 5
Consumed: 4
Producer-Consumer execution completed.

Method 2: Using Semaphores

This approach uses POSIX semaphores to control access and synchronization

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

#define BUFFER_SIZE 3
#define MAX_ITEMS 8

int buffer[BUFFER_SIZE];
int in = 0;
int out = 0;
int produced_count = 0;
int consumed_count = 0;

sem_t mutex;
sem_t full;
sem_t empty;

void* producer(void* arg) {
    int item = 100;
    
    while (produced_count < MAX_ITEMS) {
        sem_wait(&empty);
        sem_wait(&mutex);
        
        buffer[in] = item;
        printf("Produced: %d at position %d<br>", item, in);
        in = (in + 1) % BUFFER_SIZE;
        item += 10;
        produced_count++;
        
        sem_post(&mutex);
        sem_post(&full);
        
        usleep(200000); /* 0.2 second delay */
    }
    
    return NULL;
}

void* consumer(void* arg) {
    while (consumed_count < MAX_ITEMS) {
        sem_wait(&full);
        sem_wait(&mutex);
        
        int item = buffer[out];
        printf("Consumed: %d from position %d<br>", item, out);
        out = (out + 1) % BUFFER_SIZE;
        consumed_count++;
        
        sem_post(&mutex);
        sem_post(&empty);
        
        usleep(300000); /* 0.3 second delay */
    }
    
    return NULL;
}

int main() {
    pthread_t producer_thread, consumer_thread;
    
    sem_init(&mutex, 0, 1);
    sem_init(&full, 0, 0);
    sem_init(&empty, 0, BUFFER_SIZE);
    
    pthread_create(&producer_thread, NULL, producer, NULL);
    pthread_create(&consumer_thread, NULL, consumer, NULL);
    
    pthread_join(producer_thread, NULL);
    pthread_join(consumer_thread, NULL);
    
    sem_destroy(&mutex);
    sem_destroy(&full);
    sem_destroy(&empty);
    
    printf("Semaphore-based execution completed.<br>");
    return 0;
}
Produced: 100 at position 0
Produced: 110 at position 1
Consumed: 100 from position 0
Produced: 120 at position 2
Consumed: 110 from position 1
Produced: 130 at position 0
Consumed: 120 from position 2
Semaphore-based execution completed.

Key Differences

Aspect Mutex + Condition Variables Semaphores
Complexity More complex, explicit waiting Simpler, automatic blocking
Performance Slightly better for complex logic Better for simple counting
Portability POSIX threads POSIX semaphores

Common Issues

  • Deadlock Incorrect order of acquiring locks
  • Race Conditions Missing synchronization on shared variables
  • Buffer Overflow/Underflow Improper boundary checking

Conclusion

The producer-consumer problem demonstrates fundamental concepts in concurrent programming. Both mutex-condition variable and semaphore approaches provide effective solutions, with the choice depending on specific requirements and complexity needs.

Updated on: 2026-03-15T14:31:49+05:30

30K+ Views

Kickstart Your Career

Get certified by completing the course

Get Started
Advertisements