Open In App

Producer Consumer Problem in C

Improve
Improve
Improve
Like Article
Like
Save Article
Save
Share
Report issue
Report

Concurrency is an important topic in concurrent programming since it allows us to completely understand how the systems work. Among the several challenges faced by practitioners working with these systems, there is a major synchronization issue which is the producer-consumer problem. In this article, we will discuss this problem and look at possible solutions based on C programming.

What is the Producer-Consumer Problem?

The producer-consumer problem is an example of a multi-process synchronization problem. The problem describes two processes, the producer and the consumer that share a common fixed-size buffer and use it as a queue.

  • The producer’s job is to generate data, put it into the buffer, and start again.
  • At the same time, the consumer is consuming the data (i.e., removing it from the buffer), one piece at a time.

What is the Actual Problem?

Given the common fixed-size buffer, the task is to make sure that the producer can’t add data into the buffer when it is full and the consumer can’t remove data from an empty buffer. Accessing memory buffers should not be allowed to producers and consumers at the same time.

Producer Consumer Problem in OS

Producer Consumer Problem

Solution of Producer-Consumer Problem

The producer is to either go to sleep or discard data if the buffer is full. The next time the consumer removes an item from the buffer, it notifies the producer, who starts to fill the buffer again. In the same manner, the consumer can go to sleep if it finds the buffer to be empty. The next time the producer transfer data into the buffer, it wakes up the sleeping consumer.

Note: An inadequate solution could result in a deadlock where both processes are waiting to be awakened.

Approach: The idea is to use the concept of parallel programming and Critical Section to implement the Producer-Consumer problem in C language using OpenMP. 

Below is the implementation of the above approach:

C
// C program for the above approach
#include <stdio.h>
#include <stdlib.h>

// Initialize a mutex to 1
int mutex = 1;

// Number of full slots as 0
int full = 0;

// Number of empty slots as size
// of buffer
int empty = 10, x = 0;

// Function to produce an item and
// add it to the buffer
void producer()
{
    // Decrease mutex value by 1
    --mutex;

    // Increase the number of full
    // slots by 1
    ++full;

    // Decrease the number of empty
    // slots by 1
    --empty;

    // Item produced
    x++;
    printf("\nProducer produces"
           "item %d",
           x);

    // Increase mutex value by 1
    ++mutex;
}

// Function to consume an item and
// remove it from buffer
void consumer()
{
    // Decrease mutex value by 1
    --mutex;

    // Decrease the number of full
    // slots by 1
    --full;

    // Increase the number of empty
    // slots by 1
    ++empty;
    printf("\nConsumer consumes "
           "item %d",
           x);
    x--;

    // Increase mutex value by 1
    ++mutex;
}

// Driver Code
int main()
{
    int n, i;
    printf("\n1. Press 1 for Producer"
           "\n2. Press 2 for Consumer"
           "\n3. Press 3 for Exit");

// Using '#pragma omp parallel for'
// can  give wrong value due to
// synchronization issues.

// 'critical' specifies that code is
// executed by only one thread at a
// time i.e., only one thread enters
// the critical section at a given time
#pragma omp critical

    for (i = 1; i > 0; i++) {

        printf("\nEnter your choice:");
        scanf("%d", &n);

        // Switch Cases
        switch (n) {
        case 1:

            // If mutex is 1 and empty
            // is non-zero, then it is
            // possible to produce
            if ((mutex == 1)
                && (empty != 0)) {
                producer();
            }

            // Otherwise, print buffer
            // is full
            else {
                printf("Buffer is full!");
            }
            break;

        case 2:

            // If mutex is 1 and full
            // is non-zero, then it is
            // possible to consume
            if ((mutex == 1)
                && (full != 0)) {
                consumer();
            }

            // Otherwise, print Buffer
            // is empty
            else {
                printf("Buffer is empty!");
            }
            break;

        // Exit Condition
        case 3:
            exit(0);
            break;
        }
    }
}

Output:

Problem for Practice

Question 1: Processes P1 and P2 have a producer-consumer relationship, communicating by the use of a set of shared buffers.

P1: repeat
Obtain an empty buffer
Fill it
Return a full buffer
forever
P2: repeat
Obtain a full buffer
Empty it
Return an empty buffer
forever

Increasing the number of buffers is likely to do which of the following? [ISRO CS 2018]

I. Increase the rate at which requests are satisfied (throughput).

II. Decrease the likelihood of deadlock .

III. Increase the ease of achieving a correct implementation.

 (A) Ill only

 (B) II only

(C) I only 

(D) II and III only

Solution: Increasing the size of the memory allocated to the process or increasing buffer requirement does not affect the likelihood  of the
deadlock and doesn't affect the implementation of the system. It can increase the rate at which the requests are satisfied(throughput) larger
will be the size of the buffer, larger will be throughput. Therefore the only statement correct is I. Hence option (C) is correct.

Frequently Asked Question on Producer Consumer Problem – FAQs

 How do we prevent a race condition in producer-consumer code?

To avoid a race condition in producer-consumer problem, ensure that shared resources are synchronised properly.

What is a bounded buffer?

Bounded buffer is a fix size queue, that is shared between producer and consumer and this buffer has limited capacity to store data items.

Why is the producer-consumer problem relevant in operating systems?

The Producer-Consumer problem is a classic synchronisation issue in operating systems. The presence of multiple processes and limited resources in the system creates a synchronisation difficulty. If a resource is shared by multiple processes at the same time, it might lead to data inconsistency.



Last Updated : 12 Mar, 2024
Like Article
Save Article
Previous
Next
Share your thoughts in the comments
Similar Reads