Files
freemo d9e5668cec fix(skills): comprehensive final audit pass for programming-patterns skill
Fixes and improvements from exhaustive audit:

Consistency fixes in SKILL.md:
- 'Pipe & Filter' → 'Pipe and Filter' (one stray '&' found and corrected)
- 'Singleton for factory instance' → clarified to 'register factory as
  singleton-scoped via DI container' (less misleading wording)
- Documentation Format section updated with note that SKILL.md itself is the
  authoritative source for related-pattern combinations

Coverage fix — Related Patterns sections:
- Added '## Related Patterns' to ALL 94 pattern files (was 0/94)
- Each section lists 3–6 related patterns with relationship descriptions
- Covers: why they're related, when to prefer one vs the other,
  and which are often confused

SOLID principles → Creational → Structural → Behavioral → Architectural →
Concurrency → Functional → Resilience → Data Access → Messaging →
Testing → Error Handling → Microservice — all 13 categories covered

Code verification:
- Python: 0 failures (all 85 testable blocks pass)
- Go: 0 failures (all 76 testable blocks pass)
- JavaScript: 0 failures (all 78 testable blocks pass)
- All 239 code blocks verified correct after edits

Final skill state:
- 108 files, 36,524 lines across 13 reference categories
- 94/94 pattern files have Related Patterns sections
- 2,815-line SKILL.md with 67 decision trees, 23 scenarios,
  0 broken references, 0 naming inconsistencies
2026-04-15 13:22:13 -04:00

4.8 KiB

Thread Pool Pattern

Problem

Creating a new thread for every task is expensive (memory allocation, OS scheduling overhead). Under high load, unbounded thread creation can exhaust system resources.

Solution

Pre-allocate a fixed number of worker threads. Tasks are submitted to a queue, and idle workers pick up tasks. This bounds resource usage and amortizes thread creation cost across many tasks.

When to Use

  • Server request handling with bounded concurrency
  • Batch processing with many small, independent tasks
  • CPU-bound work that benefits from parallelism up to core count
  • Limiting concurrent access to a shared resource

When to Avoid

  • Tasks that are long-lived or block indefinitely (starves other tasks)
  • When task count is very small (overhead of pool management not justified)
  • Real-time systems where thread scheduling latency is critical

Pseudocode

pool = ThreadPool(workers=4)

function task(id):
    print("Task " + id + " running on " + current_thread())
    sleep(random)
    print("Task " + id + " done")

for i in 1..10:
    pool.submit(task, i)

pool.shutdown()
print("All tasks complete")

Python

from concurrent.futures import ThreadPoolExecutor, as_completed
import threading
import time
import random

def task(task_id: int) -> str:
    thread_name = threading.current_thread().name
    print(f"[Task {task_id}] Started on {thread_name}")
    duration = random.uniform(0.1, 0.3)
    time.sleep(duration)
    result = f"Task {task_id} completed in {duration:.2f}s"
    print(f"[Task {task_id}] Finished")
    return result

def main() -> None:
    with ThreadPoolExecutor(max_workers=4, thread_name_prefix="Worker") as pool:
        futures = {pool.submit(task, i): i for i in range(10)}

        for future in as_completed(futures):
            task_id = futures[future]
            result = future.result()
            print(f"  Result: {result}")

    print("All tasks complete.")

if __name__ == "__main__":
    main()

Go

package main

import (
	"fmt"
	"math/rand"
	"sync"
	"time"
)

func worker(id int, tasks <-chan int, wg *sync.WaitGroup) {
	defer wg.Done()
	for taskID := range tasks {
		fmt.Printf("[Worker %d] Processing task %d\n", id, taskID)
		duration := time.Duration(100+rand.Intn(200)) * time.Millisecond
		time.Sleep(duration)
		fmt.Printf("[Worker %d] Finished task %d in %v\n", id, taskID, duration)
	}
}

func main() {
	const numWorkers = 4
	const numTasks = 10

	tasks := make(chan int, numTasks)
	var wg sync.WaitGroup

	// Start workers
	for i := 0; i < numWorkers; i++ {
		wg.Add(1)
		go worker(i, tasks, &wg)
	}

	// Submit tasks
	for i := 0; i < numTasks; i++ {
		tasks <- i
	}
	close(tasks)

	wg.Wait()
	fmt.Println("All tasks complete.")
}

JavaScript

const sleep = (ms) => new Promise((r) => setTimeout(r, ms));

class WorkerPool {
  constructor(concurrency) {
    this.concurrency = concurrency;
    this.queue = [];
    this.active = 0;
    this.resolveIdle = null;
  }

  submit(taskFn) {
    return new Promise((resolve, reject) => {
      this.queue.push({ taskFn, resolve, reject });
      this._tryRun();
    });
  }

  _tryRun() {
    while (this.active < this.concurrency && this.queue.length > 0) {
      const { taskFn, resolve, reject } = this.queue.shift();
      this.active++;
      taskFn()
        .then(resolve)
        .catch(reject)
        .finally(() => {
          this.active--;
          this._tryRun();
          if (this.active === 0 && this.queue.length === 0 && this.resolveIdle) {
            this.resolveIdle();
          }
        });
    }
  }

  async waitIdle() {
    if (this.active === 0 && this.queue.length === 0) return;
    return new Promise((r) => (this.resolveIdle = r));
  }
}

async function task(id) {
  const worker = `Worker-${id % 4}`;
  console.log(`[${worker}] Processing task ${id}`);
  const duration = 100 + Math.random() * 200;
  await sleep(duration);
  console.log(`[${worker}] Finished task ${id} in ${duration.toFixed(0)}ms`);
  return `Task ${id} done`;
}

async function main() {
  const pool = new WorkerPool(4);
  const results = [];

  for (let i = 0; i < 10; i++) {
    results.push(pool.submit(() => task(i)));
  }

  const allResults = await Promise.all(results);
  allResults.forEach((r) => console.log(`  Result: ${r}`));
  console.log("All tasks complete.");
}

main();
  • Producer-Consumer — the thread pool workers are the consumers; tasks submitted to the pool are produced by the caller.
  • Object Pool — a thread pool is a specialised object pool where the pooled resource is a thread.
  • Future/Promise — task submission to a thread pool typically returns a future representing the eventual result.
  • Bulkhead — a fixed-size thread pool implements a bulkhead, isolating resource consumption per dependency.