Learning outcomes
- Solve the producer–consumer problem using appropriate synchronisation tools.
The Producer–Consumer problem is one of the most important classical synchronization problems in operating systems. It models a situation where one or more producer processes generate data items and place them into a shared buffer, while one or more consumer processes remove those items and use them. The challenge is to ensure correct coordination when both producers and consumers access the same shared buffer concurrently.
Suppose there is a finite buffer of size N. A producer inserts items into the buffer, and a consumer removes items from it. Since the buffer is shared, simultaneous access must be controlled.
A correct solution must satisfy the following:
The producer–consumer problem appears in many real systems:
This problem is also known as the bounded-buffer problem when the shared buffer has limited capacity.
If synchronization is not used, the system may suffer from:
A common implementation uses a circular buffer:
A standard semaphore-based solution uses three synchronization variables:
| Semaphore / Variable | Meaning |
|---|---|
| mutex | Ensures mutual exclusion while accessing the buffer |
| empty | Counts how many empty slots remain in the buffer |
| full | Counts how many filled slots are present in the buffer |
Initial values for a buffer of size N are:
do {
item = produce_item();
wait(empty); // wait if buffer is full
wait(mutex); // enter critical section
insert_item(item);
signal(mutex); // leave critical section
signal(full); // one more filled slot
} while(true);
do {
wait(full); // wait if buffer is empty
wait(mutex); // enter critical section
item = remove_item();
signal(mutex); // leave critical section
signal(empty); // one more empty slot
consume_item(item);
} while(true);
wait(empty).wait(mutex) to enter the critical section safely.signal(mutex).full to indicate that one more item is available.wait(full).wait(mutex).mutex and then empty.empty prevents overflow,
full prevents underflow, and
mutex prevents simultaneous modification of the shared buffer.
| Requirement | How it is satisfied |
|---|---|
| No simultaneous buffer corruption | mutex ensures only one process enters the critical section at a time |
| No producer overflow | empty blocks producer when no free slot exists |
| No consumer underflow | full blocks consumer when no item exists |
| Proper synchronization | Semaphore counts reflect actual buffer state |
| Case | Meaning | Effect on synchronization |
|---|---|---|
| Bounded Buffer | Buffer has limited size | Producer may need to wait if buffer is full |
| Unbounded Buffer | Buffer is assumed large enough to never fill | Producer never waits for space, but consumer still waits for items |
In practice, most real systems use bounded buffers because memory is limited.
Although semaphores are the classical solution, the producer–consumer problem can also be solved using:
Suppose buffer size = 3. Initially:
If the producer inserts one item:
wait(empty) → empty becomes 2wait(mutex) → mutex becomes 0signal(mutex) → mutex becomes 1signal(full) → full becomes 1Now the consumer can remove the item:
wait(full) → full becomes 0wait(mutex) → mutex becomes 0signal(mutex) → mutex becomes 1signal(empty) → empty becomes 3So the shared buffer state remains correct throughout.
mutex but not tracking full/empty slotswait and signal matters.
| Element | Role in producer–consumer solution |
|---|---|
| Producer | Generates and inserts items |
| Consumer | Removes and uses items |
| Shared buffer | Temporary storage between producer and consumer |
| mutex | Protects the critical section |
| empty | Counts available empty slots |
| full | Counts available filled slots |
The Producer–Consumer problem models the synchronization of processes sharing a common buffer. Producers insert items and consumers remove them.
A correct solution must prevent: buffer overflow, buffer underflow, and race conditions.
The standard semaphore solution uses: mutex for mutual exclusion, empty to count free slots, and full to count occupied slots.
This is one of the most important classical synchronization problems because it captures real operating-system situations involving shared queues, bounded resources, and coordinated process behavior.
mutex, empty, and full semaphores?