Start here. This is the direct spoken answer to practice first.
Overview
Compares convenient context-propagating thread offload with explicit executor selection and ownership.
asyncio.to_thread() is a convenient way to run a blocking function in a thread without blocking the event-loop thread, and it propagates the current contextvars.Context. run_in_executor() is lower-level and lets code choose the default executor or an explicit thread, process, or interpreter executor. Both return awaitable completion; neither turns the function itself into cooperative async code.