ConcurrencyPythonverifiedVerified

Thread Pool Pattern in Python

Maintain a fixed set of reusable worker threads that pick up tasks from a queue, avoiding the overhead of spawning a new thread per task.

How to Implement the Thread Pool Pattern in Python

1Step 1: Define the task type and implement the pool using asyncio

import asyncio
from typing import Callable, Awaitable, TypeVar

T = TypeVar("T")
Task = Callable[[], Awaitable[T]]

2Step 2: Implement the pool with a semaphore for concurrency control

class AsyncPool:
    def __init__(self, size: int) -> None:
        self._semaphore = asyncio.Semaphore(size)
        self._pending = 0
        self._active = 0

    async def submit(self, task: Task[T]) -> T:
        self._pending += 1
        async with self._semaphore:
            self._pending -= 1
            self._active += 1
            try:
                return await task()
            finally:
                self._active -= 1

    @property
    def pending(self) -> int:
        return self._pending

    @property
    def active(self) -> int:
        return self._active

3Step 3: Submit tasks and observe pooled execution

async def main() -> None:
    pool = AsyncPool(4)

    async def work(i: int) -> str:
        await asyncio.sleep(0.01)
        return f"task-{i}-done"

    results = await asyncio.gather(
        *(pool.submit(lambda i=i: work(i)) for i in range(10))
    )
    print(results)


if __name__ == "__main__":
    asyncio.run(main())

Thread Pool Pattern Architecture

hourglass_empty

Rendering diagram...

lightbulb

Thread Pool Pattern in the Real World

A hotel concierge desk staffed by three concierges represents the thread pool. No matter how many guests check in, only three requests are handled simultaneously. Other guests wait in the lobby queue. When a concierge finishes, they immediately assist the next waiting guest — the staff are never created or dismissed per guest, they simply stay on duty.