Python › Concurrency (asyncio/threading/multiprocessing)

How do you safely run a blocking, synchronous function from within an asyncio coroutine without freezing the event loop?

Advanced

Answer

Use loop.run_in_executor() (or the higher-level asyncio.to_thread() in modern Python) to offload the blocking call to a separate thread pool, awaiting its completion WITHOUT blocking the event loop itself — appropriate for blocking I/O calls or moderate CPU work that can't be rewritten as native async code.
import asyncio

def blocking_io():
    import time; time.sleep(2)  # simulates a blocking call
    return "done"

async def main():
    result = await asyncio.to_thread(blocking_io)  # runs in a thread, doesn't block the event loop
    print(result)
Real-world example Calling a legacy synchronous database driver or third-party library from inside an otherwise fully async application.

Common follow-ups: When would you use a ProcessPoolExecutor instead of a ThreadPoolExecutor for offloading work from asyncio?