asyncio.to_thread runs blocking functions in a separate thread.
1import asyncio2import time34# Blocking function5def blocking_io():6 time.sleep(2) # Simulate blocking I/O7 return "Data from disk"89def cpu_bound():10 return sum(i * i for i in range(10**7))1112async def main():13 # Run blocking functions without blocking event loop14 result = await asyncio.to_thread(blocking_io)15 print(result)1617 # With arguments18 result = await asyncio.to_thread(cpu_bound)19 print(result)2021 # Multiple threads22 results = await asyncio.gather(23 asyncio.to_thread(blocking_io),24 asyncio.to_thread(blocking_io),25 )26 print(results) # Runs concurrently2728asyncio.run(main())
vs run_in_executor:
to_thread is simpler (no executor needed).run_in_executor gives more control.Use cases: