# 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

```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

```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

```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();
```

## Related Patterns

- **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.
