api: YAML JSON TOON Database
version: 1.0.0
format: yaml
dataset:
  id: 354
  slug: concurrency-design-patterns
  title: Concurrency Design Patterns
  description: "Patterns for multi-threaded programming: Producer-Consumer, Reader-Writer, Thread Pool, Future/Promise, Reactor, Monitor, Scheduler."
  category: Design Patterns
  category_slug: design-patterns
  tags: design-patterns,concurrency,multithreading,producer-consumer,thread-pool
  view_count: 5
  created_at: 1781275786
  updated_at: 1781275786
data:
  patterns:
    - name: Producer-Consumer (Bounded Buffer)
      problem: Producers generate data; consumers process it. Need to coordinate so consumers don't read from empty buffer, producers don't write to full buffer.
      solution_brief: Use a fixed-size queue as buffer. Producers block when full; consumers block when empty. Synchronized access ensures thread safety.
      key_components:
        - Shared buffer/queue with bounded capacity
        - Producer threads (generate items, put in buffer)
        - Consumer threads (take items from buffer, process)
        - Synchronization mechanism (mutex + condition variables/semaphores)
      benefits: Decouples producers and consumers, smooths load variations, enables pipelining, buffers spikes in production/consumption rates.
      pitfalls: Deadlock if synchronization incorrect, priority inversion possible, buffer size tuning critical (too small causes blocking; too large uses memory), consumers may starve if producers fast.
      language_examples:
        Java: BlockingQueue<E> interface (ArrayBlockingQueue, LinkedBlockingQueue); put() and take() block automatically.
        Python: queue.Queue (thread-safe), producer calls put(), consumer calls get()
        C++: "std::queue with std::mutex + std::condition_variable, or boost::lockfree::queue"
        Go: Channels (chan) with buffered or unbuffered; goroutines communicate naturally
    - name: Reader-Writer Lock
      problem: Data structure frequently read, rarely written. Mutex allows only one thread at a time (even for readers), causing unnecessary contention.
      solution_brief: Allow multiple readers simultaneous access, but writers get exclusive access. Readers don't block each other; writers block both readers and other writers.
      key_components:
        - "Read lock (shared): multiple threads can hold simultaneously"
        - "Write lock (exclusive): only one thread, no readers"
        - Lock upgrade/downgrade (optional)
        - Reader counter or reference count
      benefits: Better throughput for read-heavy workloads, scales well with many readers, writers get exclusive access to prevent dirty reads.
      pitfalls: Writer starvation if continuous readers arrive, reader-writer deadlock if not careful, higher overhead than simple mutex for write-heavy cases, complex to implement correctly.
      language_examples:
        Java: java.util.concurrent.locks.ReentrantReadWriteLock; readLock().lock(), writeLock().lock()
        C++: "std::shared_mutex (C++17); std::shared_lock for readers, std::unique_lock for writers"
        Python: threading.RLock doesn't support readers; use read_write_lock third-party or multiprocessing.Manager
        C#: ReaderWriterLockSlim; EnterReadLock(), EnterWriteLock()
    - name: Thread Pool
      problem: Creating/destroying threads is expensive (OS resources, context switching). Spawning thread per task leads to overhead and uncontrolled resource usage.
      solution_brief: Pre-create a fixed number of worker threads and queue tasks. Workers wait for tasks; when task arrives, idle worker executes it. Reuses threads across many tasks.
      key_components:
        - Thread pool (fixed size worker threads)
        - Task queue (work queue, blocking)
        - "Worker loop: while (true) { task = queue.take(); execute(task); }"
        - "Saturation strategy (queue full: reject, block, or spawn new thread)"
      benefits: Reduces thread creation overhead, bounds resource usage, improves response time (threads ready), enables task prioritization and scheduling.
      pitfalls: Queue can become bottleneck, thread starvation if pool too small, deadlocks if tasks wait on other queued tasks, tasks may block pool threads indefinitely.
      language_examples:
        Java: ExecutorService, ThreadPoolExecutor, Executors.newFixedThreadPool(n), CompletableFuture with custom executor
        Python: concurrent.futures.ThreadPoolExecutor, submit(task) returns Future
        C#: Task Parallel Library (TPL), ThreadPool.QueueUserWorkItem, Task.Run
        C++: "Boost.Asio thread pool, Intel TBB task scheduler, custom with std::thread + std::queue"
    - name: Future / Promise
      problem: Need to run computation asynchronously and retrieve result later. Caller shouldn't block waiting for result; needs way to check completion or get result when ready.
      solution_brief: Future is a placeholder for result of async operation. Promise is writeable handle that fulfills the future. Caller gets future immediately, checks isDone(), calls get() to block or poll.
      key_components:
        - "Future (read-only): isDone(), get(timeout), cancel()"
        - "Promise (write-only): setValue(result), setException(err)"
        - "Completion callback (optional): then(), addListener()"
        - Cancellation mechanism
      benefits: Avoids blocking threads, enables composition of async operations, simplifies error propagation, clean API for async results.
      pitfalls: Blocking get() defeats purpose (use callbacks instead), exceptions must be captured in Future, cancellation not always supported, many small async tasks create overhead.
      language_examples:
        Java: CompletableFuture<T> (most powerful), Future<T> (older), CompletableFuture.supplyAsync(...)
        JavaScript: Promise (then/catch), async/await syntactic sugar
        Python: concurrent.futures.Future, asyncio.Future
        C#: Task<T> (Task is Future without result); async/await
    - name: Reactor Pattern
      problem: Server handling many simultaneous connections (e.g., web server). One thread per connection (thread-per-connection) doesn't scale (thousands of connections = thousands of threads).
      solution_brief: Single (or few) thread(s) use non-blocking I/O and demultiplexer (select/poll/epoll/kqueue) to monitor multiple sockets. When I/O event occurs, dispatcher calls appropriate handler callback.
      key_components:
        - Event demultiplexer (select, poll, epoll, kqueue)
        - Event handlers (callback objects)
        - "Reactor (main loop: wait for events -> dispatch to handlers)"
        - Non-blocking I/O sockets
      benefits: Scales to thousands of connections with few threads, efficient resource usage (no thread overhead), good for I/O-bound services.
      pitfalls: Complex to implement correctly, callbacks can lead to spaghetti code (use coroutines/fibers), CPU-bound tasks still block reactor, edge-triggered vs level-triggered pitfalls.
      language_examples:
        Java: NIO (Non-blocking I/O), Selector, SocketChannel; Netty framework implements Reactor
        Python: selectors module, asyncio event loop, Twisted framework
        Node.js: libuv event loop (epoll/kqueue/IOCP) built-in; all I/O non-blocking
        C++: Boost.Asio, libevent, libuv
    - name: Monitor (Monitor Object)
      problem: Multiple threads access shared data; need to ensure mutual exclusion and coordinate access. Condition variables and mutexes scattered lead to error-prone code.
      solution_brief: Encapsulate shared data with its synchronization (mutex) and condition variables inside a monitor object. Only one thread can execute any monitor method at a time. Condition variables allow threads to wait for conditions.
      key_components:
        - Mutual exclusion lock (implicit in monitor entry)
        - Condition variables (wait/signal)
        - Shared data (private to monitor)
        - Procedures/methods (only one active at a time)
      benefits: Simplifies synchronization (acquire/release implicit), prevents unsynchronized access (all accesses go through monitor), high-level abstraction, reduces race conditions.
      pitfalls: Can cause performance bottleneck if monitor too large (coarse-grained), conditional waiting can lead to missed signals or spurious wakeups, deadlocks possible if monitor calls external code.
      language_examples:
        Java: synchronized methods/blocks (built-in monitor), wait()/notify()/notifyAll() on object
        Python: threading.Lock + Condition, but no built-in monitor keyword; can use with statement
        C#: lock statement (Monitor.Enter/Exit), Monitor.Wait/Pulse
        C++: "No built-in monitor; implement with std::mutex + std::condition_variable"
    - name: Scheduler Pattern
      problem: Need to execute tasks at specific times (cron jobs), at fixed intervals, or after delays. Managing timers and thread pools manually is complex.
      solution_brief: Central scheduler coordinates task execution based on time criteria. Maintains priority queue of scheduled tasks; scheduler thread manages timers and dispatches tasks to worker threads when due.
      key_components:
        - Task (callable with execution time, priority, periodicity)
        - Schedule queue (priority queue ordered by next execution time)
        - Timer mechanism (wait/notify, delay queue, wheel timer)
        - Worker pool (optional, execute tasks asynchronously)
        - Scheduling strategies (fixed-rate, fixed-delay, cron)
      benefits: Centralized task management, efficient timer management (single thread for many timers), supports recurring tasks, cron-like scheduling, easy to pause/cancel/reschedule.
      pitfalls: Scheduler thread single point of failure, task overruns can backlog, clock drift affects timing, distributed systems need distributed scheduler (e.g., Quartz, Celery).
      language_examples:
        Java: ScheduledExecutorService, Timer/TimerTask, Quartz Scheduler, Spring @Scheduled
        Python: sched module, APScheduler, Celery beat, cron jobs via OS
        C#: System.Threading.Timer, Quartz.NET, Hangfire
        Node.js: node-cron, setInterval/setTimeout (single-threaded)
