Producer-consumer using queue
Producer-consumer uses a shared bounded queue buffer: producers enqueue items, consumers dequeue items. Full and empty states control when each side waits.
On this page
- Bounded buffer model
- Queue operations in synchronization context
- C simulation (single-threaded demo)
1) Buffer states
| State | Producer action | Consumer action |
|---|---|---|
| Empty | Can produce | Must wait |
| Partial | Can produce | Can consume |
| Full | Must wait | Can consume |
2) Example in C (queue simulation)
#include <stdio.h>
#define CAP 5
int q[CAP];
int f = 0; // front index
int r = -1; // rear index
int sz = 0; // current number of items in buffer
int is_full(void) {
return sz == CAP;
}
int is_empty(void) {
return sz == 0;
}
// Producer inserts one item into bounded circular queue
void produce(int x) {
if (is_full()) {
printf("Producer waits (full)\n");
return;
}
r = (r + 1) % CAP;
q[r] = x;
sz++;
printf("Produced %d\n", x);
}
// Consumer removes one item from bounded circular queue
void consume(void) {
if (is_empty()) {
printf("Consumer waits (empty)\n");
return;
}
int x = q[f];
f = (f + 1) % CAP;
sz--;
printf("Consumed %d\n", x);
}
int main(void) {
// Demo event sequence (single-thread simulation)
produce(10);
produce(20);
consume();
produce(30);
produce(40);
produce(50);
produce(60);
consume();
consume();
consume();
return 0;
}
Initial state
| Variable | Value |
|---|---|
f (front) | 0 |
r (rear) | -1 |
sz (size) | 0 |
| Queue array | [?, ?, ?, ?, ?] |
CAP | 5 |
Complete execution table
| Step | Operation | f | r | sz | Queue Array (indices 0-4) | Output |
|---|---|---|---|---|---|---|
| 0 | Initial | 0 | -1 | 0 | [?, ?, ?, ?, ?] | - |
| 1 | produce(10) | 0 | 0 | 1 | [10, ?, ?, ?, ?] | Produced 10 |
| 2 | produce(20) | 0 | 1 | 2 | [10, 20, ?, ?, ?] | Produced 20 |
| 3 | consume() | 1 | 1 | 1 | [10, 20, ?, ?, ?] | Consumed 10 |
| 4 | produce(30) | 1 | 2 | 2 | [10, 20, 30, ?, ?] | Produced 30 |
| 5 | produce(40) | 1 | 3 | 3 | [10, 20, 30, 40, ?] | Produced 40 |
| 6 | produce(50) | 1 | 4 | 4 | [10, 20, 30, 40, 50] | Produced 50 |
| 7 | produce(60) | 1 | 0 | 5 | [60, 20, 30, 40, 50] | Produced 60 |
| 8 | consume() | 2 | 0 | 4 | [60, 20, 30, 40, 50] | Consumed 20 |
| 9 | consume() | 3 | 0 | 3 | [60, 20, 30, 40, 50] | Consumed 30 |
| 10 | consume() | 4 | 0 | 2 | [60, 20, 30, 40, 50] | Consumed 40 |
Queue wrapping visualization
After Step 6 (before produce(60)):
Indices: 0 1 2 3 4
Values: [10, 20, 30, 40, 50]
↑ ↑
f r
After Step 7 (produce(60) wraps to index 0):
Indices: 0 1 2 3 4
Values: [60, 20, 30, 40, 50]
↑ ↑
r f
After final consume (step 10):
Indices: 0 1 2 3 4
Values: [60, 20, 30, 40, 50]
↑
f (points to next to consume)
↑
r (rear points to last produced)
Final output
Produced 10 Produced 20 Consumed 10 Produced 30 Produced 40 Produced 50 Produced 60 Consumed 20 Consumed 30 Consumed 40
Key observations
| Aspect | Value/Behavior |
|---|---|
| Queue Type | Circular buffer (array-based) |
| Capacity | 5 items maximum |
| Full condition | sz == CAP |
| Empty condition | sz == 0 |
| Wrap-around | When r = 4, next produce wraps to index 0 |
| Producer blocking | Only prints message (simulation, not actual blocking) |
| Consumer blocking | Only prints message when empty |
| FIFO Order | Maintained even with wrap-around |
| Remaining items | After step 10: sz=2 (items 50 and 60 remain in queue) |
Note: This is a single-thread simulation of producer-consumer behavior. In a real multi-threaded environment, produce() and consume() would block/wait when full/empty instead of just printing messages.
3) Complexity
- enqueue/dequeue: O(1)
- buffer space: O(CAP)
Key takeaway
Producer-consumer is a queue-centered concurrency pattern. In multithreaded versions, combine this queue logic with mutex + condition variables/semaphores.