threading.local separates threads, but it does not reset state between jobs on the same worker.
Make this comfortable
Python thread-local state: a reused pool worker retains the previous job's value
Operation contract
A one-worker pool processes a first receipt and stores its ID in thread-local state. The next job runs on the same worker and sees the old ID before clearing it. The fixture confirms the worker reuse contract without depending on which operating-system thread ID was assigned.
Failure boundary
This is a single-worker demonstration; larger pools make job-to-worker assignment nondeterministic. Thread-local state is not request-local state. Initialize and clear it around each job, or pass context explicitly when work can migrate between threads.
Working program
from concurrent.futures import ThreadPoolExecutor
from threading import local
worker_state = local()
def record_receipt():
worker_state.receipt_id = "R-47"
return worker_state.receipt_id
def inspect_next_job():
previous = getattr(worker_state, "receipt_id", None)
if hasattr(worker_state, "receipt_id"):
del worker_state.receipt_id
return previous, getattr(worker_state, "receipt_id", None)
with ThreadPoolExecutor(max_workers=1) as pool:
print("first", pool.submit(record_receipt).result())
print("next", pool.submit(inspect_next_job).result())Output
first R-47
next ('R-47', None)Costs and limits
A thread-local lookup is cheap; retained values can live as long as a pool thread and keep referenced objects alive. Explicit per-job cleanup prevents cross-request retention.
Common Mistakes
- Thread isolation is not job isolation in a reusable executor.
- A pool's next job may inherit stale state from that worker.
- Do not use a thread-local field as a substitute for an explicit request argument.
Connected lessons
python
thread-local-pool-reuse
