Cancelling a task awaiting to_thread does not interrupt a function already running in an OS thread.
Make this comfortable
Python asyncio.to_thread cancellation: the blocking worker can outlive its awaiter
Operation contract
A blocking export signals that it started, then waits on an owned threading.Event. The async task awaiting it is cancelled; the export is still blocked. The owner releases the worker, waits for its completion signal, and allows asyncio.run to shut down its executor cleanly.
Failure boundary
This uses a controllable local function, not a real file or network operation. A production blocking call needs its own stop signal, deadline, and idempotent cleanup. Abandoning its async awaiter does not transfer those responsibilities to the event loop.
Working program
import asyncio
from threading import Event
started = Event()
release = Event()
finished = Event()
def blocking_receipt_export():
started.set()
release.wait(timeout=5)
finished.set()
return "R-47"
async def cancel_export_awaiter():
export_task = asyncio.create_task(asyncio.to_thread(blocking_receipt_export))
try:
if not await asyncio.to_thread(started.wait, 2):
raise TimeoutError("export did not start")
export_task.cancel()
try:
await export_task
except asyncio.CancelledError:
print("awaiter_cancelled", True)
print("worker_still_blocked", not finished.is_set())
finally:
release.set()
if not await asyncio.to_thread(finished.wait, 2):
raise TimeoutError("export did not finish")
print("worker_finished", finished.is_set())
asyncio.run(cancel_export_awaiter())Output
awaiter_cancelled True
worker_still_blocked True
worker_finished TrueCosts and limits
A blocked worker consumes one executor thread even after the awaiter is cancelled. Unbounded calls can exhaust the pool; use admission limits and cooperative stop signals around blocking work.
Common Mistakes
- Cancelling the asyncio task does not kill the OS thread.
- Release or stop the worker before shutting down the executor.
- A timeout on the awaiter does not bound a blocking function's own lifetime.
Connected lessons
python
to-thread-cancel-lifetime
