asyncio
لم يُترجم بعد؛ يُعرض باللغة الإنجليزية
asyncio lets one Python thread juggle many slow waits. Follow brew.py, a ten-line program, through the event loop: a coroutine object that does nothing until something drives it, asyncio.run(), Tasks made with asyncio.TaskGroup, tasks parking at await while their timers run, and results kept on each Task. Two waits of 2 and 1 seconds finish together in about 2 seconds. Then meet the classic trap: awaiting coroutines one after another takes the full 3.
5 أسئلة سريعة
اختر إجابة لتعرف السبب. تُحفَظ نتيجتك مع تقدّمك.
01async def fetch(): ... What does the call fetch() do on its own, outside any event loop?
Calling a coroutine function only builds a coroutine object. Its body runs when something drives it: asyncio.run(), await, or a Task. One that is never awaited triggers a RuntimeWarning when it is garbage-collected.
02Inside main(), tg.create_task(brew("tea", 2)) has just returned. What has tea done so far?
create_task() wraps the coroutine in a Task and schedules it. The loop runs one task at a time, so tea starts only when main reaches an await and gives control back.
03Two tasks in one TaskGroup await asyncio.sleep(3) and asyncio.sleep(1). About how long until the async with block ends?
Both sleeps run at the same time while the tasks are parked, so the group waits for the slowest: about 3 seconds. Cores do not matter: one thread does all the work.
04main() runs a = await brew("tea", 2) and then b = await brew("coffee", 1). About how long does it take?
Awaiting a coroutine directly runs it to the end before the next line starts, so the waits add up: 2 + 1 = 3 seconds. Wrapping them in tasks (TaskGroup or gather) overlaps them: about 2 seconds.
05A task calls time.sleep(5) instead of await asyncio.sleep(5). What happens to the other tasks?
A task hands the loop back only at await. time.sleep() blocks the one thread, so every task waits. Use await asyncio.sleep(), or run blocking work with asyncio.to_thread().
المسرد
- Coroutine function
- A function defined with async def. Calling it returns a coroutine object instead of running the body.
- Coroutine object
- A paused computation made by calling a coroutine function. It runs only when awaited, passed to asyncio.run(), or wrapped in a Task.
- await
- Waits for an awaitable (a coroutine, Task or future). While it waits, the current task is suspended and the event loop can run other tasks.
- Event loop
- The scheduler at the heart of asyncio: on one thread it runs ready tasks one at a time and wakes parked ones when their timers or sockets are ready.
- Task
- A coroutine scheduled to run on the event loop. It keeps the coroutine's return value, read with task.result() once task.done() is True.
- asyncio.run()
- Creates a new event loop, runs the given coroutine as a task until it finishes, then closes the loop. The usual entry point of an asyncio program.
- TaskGroup
- asyncio.TaskGroup (Python 3.11+): an async with block whose create_task() starts tasks; leaving the block waits for all of them, and cancels the rest if one fails.
- Concurrency
- Making progress on several jobs by switching between them. asyncio is concurrent on one thread, not parallel: it switches only at await.
الملاحظات والمصادر
- asyncio.TaskGroup needs Python 3.11 or newer. On older versions, asyncio.gather(brew("tea", 2), brew("coffee", 1)) overlaps the two coroutines in the same way and returns both results as a list.
- Strictly, the ready queue holds callbacks, not tasks: when a timer fires, the loop schedules a callback with call_soon() that resumes the parked task, and such callbacks run in the order they were scheduled.
- While every task is parked, the loop asks the operating system to wake it when a socket is ready or the next timer is due (Python's selectors module), so waiting costs no CPU.
- About 2 seconds: a little more in practice, because starting tasks and switching between them takes a small amount of time.
- If a task in a TaskGroup raises an exception, the group cancels the tasks still running, waits for them, and raises the errors together as an ExceptionGroup, which except* can catch.
- A coroutine object that is never awaited does nothing; when it is garbage-collected Python warns: RuntimeWarning: coroutine 'brew' was never awaited. The address after 0x differs on every run.
- A task hands the loop back only at an await. time.sleep() or a long calculation never awaits, so it freezes every other task; use await asyncio.sleep(), or move the work off the loop with asyncio.to_thread().
- Python library: Coroutines and tasks (asyncio.run, create_task, sleep, Task.result)
- Python library: Task groups (asyncio.TaskGroup)
- Python library: Runners (asyncio.run and asyncio.Runner)
- Python library: Event loop (call_soon order, call_later timers)
- Python library: Developing with asyncio (blocking code, one thread)
- Python reference: Coroutines (async def, async with)
- Python reference: Await expression
- PEP 492: Coroutines with async and await syntax
اختبار يتاح بعد الخطوة الأخيرة.