producer consumer pattern parallel
**Producer-Consumer Pattern** is **the fundamental concurrent design pattern where producer threads generate work items and enqueue them into a shared buffer, while consumer threads dequeue and process items — decoupling production rate from consumption rate and enabling pipeline-style parallelism across heterogeneous processing stages**.
**Buffer Designs:**
- **Bounded Blocking Queue**: fixed-capacity queue using mutex + two condition variables (not-full, not-empty); producers block when queue is full; consumers block when empty; straightforward to implement correctly but mutex contention limits throughput to ~10-50 million ops/sec
- **Lock-Free Ring Buffer (SPSC)**: single-producer single-consumer queue using atomic head/tail pointers with memory fences; producer writes data and advances tail; consumer reads data and advances head; achieves 100-500 million ops/sec by eliminating all locks
- **MPMC Lock-Free Queue**: multi-producer multi-consumer queue using CAS operations on head/tail with per-slot sequence counters; each slot carries a sequence number that producers and consumers use to claim slots atomically; Michael-Scott queue is the classic linked-list design
- **Work-Stealing Deque**: double-ended queue where the owning thread pushes/pops from one end (LIFO) and thieves steal from the other end (FIFO); Chase-Lev deque achieves lock-free operation for the common case (owner access) with CAS only for stealing
**Synchronization Strategies:**
- **Spin-Wait**: consumer spins on tail pointer until new data appears; lowest latency (<100 ns) but wastes CPU cycles — suitable only when latency is critical and cores are dedicated
- **Blocking Wait**: consumer sleeps on condition variable/futex when queue is empty; higher latency (1-10 μs wake-up) but zero CPU usage during wait — suitable for variable-rate workloads
- **Hybrid (Spin-then-Block)**: spin for a short period (1000-10000 cycles), then block; captures low-latency for frequent arrivals while avoiding CPU waste for long idle periods
- **Batch Dequeue**: consumer dequeues multiple items at once (drain the queue), processes them all, then checks for more; amortizes synchronization overhead over multiple items; 5-10× throughput improvement for high-rate producers
**Memory Ordering and Correctness:**
- **Publish Pattern**: producer writes data to buffer slot using relaxed stores, then publishes availability using a release store to the tail pointer; consumer acquires the tail pointer value, ensuring all data writes are visible
- **False Sharing Avoidance**: head and tail pointers must be on separate cache lines (64+ bytes apart) to prevent false sharing between producer and consumer cores — padding with alignment attributes is essential
- **ABA Problem**: in lock-free queues, a pointer value may be reused after deallocation and reallocation, causing CAS to succeed incorrectly; solved by tagged pointers (combining pointer with monotonic counter) or hazard pointers
**Scaling and Deployment:**
- **Multi-Stage Pipeline**: chaining producer-consumer queues creates processing pipelines; each stage runs on dedicated threads with bounded buffers providing backpressure; total throughput limited by the slowest stage (bottleneck)
- **Fan-Out/Fan-In**: one producer distributes to multiple consumer queues (parallel processing) or multiple producers feed into one consumer queue (aggregation); work distribution uses round-robin, hash-based routing, or work-stealing
- **NUMA Awareness**: queue memory and associated threads should be placed on the same NUMA node to minimize cross-socket memory traffic; for cross-NUMA pipelines, batch transfers amortize remote access latency
The producer-consumer pattern is **the backbone of nearly all concurrent systems — from operating system I/O schedulers to database query engines to GPU command queues — mastering its implementation variants and understanding the performance tradeoffs between blocking, spinning, and lock-free designs is essential for building high-throughput parallel applications**.