Concurrency Best Practices
Safety and operability rules for threads, processes, and bridges to async code.
Search across all documentation pages
Safety and operability rules for threads, processes, and bridges to async code.
max_workers or task fan-out.to_thread or process pool.cpu_count() * 5 by default.threading.local().if __name__ == "__main__". Required on spawn platforms.maxtasksperchild for leaky C extensions. Long-running pools recycle workers.task_done for every queue.get in threading.Queue pipelines. Or join hangs.result(). Silent failed futures lose errors.asyncio.run repeatedly in hot servers use lifespan.Shared mutable cache without locks plus concurrent workers - use queues or proper synchronization.
Start at cpu_count(), reduce if memory per worker is large.
Safer from data races on one thread, but blocking-the-loop causes total outage - different failure mode.
For per-request state in sync WSGI/threaded servers - not a substitute for proper async contextvars in ASGI.
Dump all thread stacks (faulthandler.dump_traceback_all) during staging hang reproduction.
itertools and operator won't fix compound business invariants - use locks or single-thread ownership.
loop.run_in_executor(process_pool, fn) - keep pool lifecycle tied to app startup/shutdown.
Maintain if required; greenfield FastAPI/asyncio avoids monkey-patching stdlib.
Shutdown, burst enqueue, and slow consumer backpressure - not only steady-state throughput.
Re-audit all practices - data races on dict/list become more likely without GIL serialization.
Stack versions: This page was written for Python 3.14.0 (stable 3.14, maintenance 3.13), FastAPI 0.115+, Django 5.2, Flask 3.1, Pydantic 2, PyTorch 2.6+, pandas 2.2+, Polars 1.x, ruff 0.9+, and uv 0.6+.
Reviewed by Chris St. John·Last updated Jul 19, 2026