Creating and Managing Tasks
Creating and Managing Tasks
While the await expression pauses the current coroutine until a single operation completes, achieving true concurrent execution requires wrapping coroutines into Tasks. An asyncio.Task schedules a coroutine on the active event loop, allowing multiple operations to interleave and run concurrently on the single thread.
Understanding task scheduling, aggregation, cancellation, and Python 3.11's modern Structured Concurrency (asyncio.TaskGroup) is essential for building production-grade asynchronous services.
1. Scheduling Concurrency: asyncio.create_task
When you simply write:
These operations execute sequentially. fetch_b() cannot begin until fetch_a() has completed.
To execute them concurrently, wrap each coroutine with asyncio.create_task():
2. Aggregating Results with asyncio.gather
The asyncio.gather() function accepts an arbitrary number of awaitables, runs them concurrently, and returns an aggregated list of their results in the exact order of submission:
Handling Failures with return_exceptions=True
By default, if one task inside gather() raises an unhandled exception, gather() immediately raises that exception, but the other tasks continue running in the background (orphan tasks). Setting return_exceptions=True causes exceptions to be returned as items in the result list alongside successful values:
3. Python 3.11+ Structured Concurrency: asyncio.TaskGroup
PEP 654 and Python 3.11 introduced Structured Concurrency via asyncio.TaskGroup. It provides an asynchronous context manager that guarantees no task leaks or orphan tasks:
4. Timeouts & Task Cancellation
Tasks can be cancelled programmatically, or automatically via timeout wrappers:
5. Architectural Summary Table
| Tool | Purpose | Failure Behavior |
|---|---|---|
asyncio.create_task(coro) | Schedules a single coroutine on loop | Unhandled errors logged as task exceptions |
asyncio.gather(*awaitables) | Concurrently aggregates results | Raises first error; siblings continue running |
asyncio.TaskGroup() (3.11+) | Structured Concurrency context | Cancels all child tasks if one fails; raises ExceptionGroup |
asyncio.timeout(delay) (3.11+) | Async context manager timeout | Cancels tasks inside scope upon deadline |
task.cancel() | Cancels an individual task | Injects asyncio.CancelledError at await point |
Multiple Choice Questions
1.
What is the effect of calling task = asyncio.create_task(my_coro())? A. It pauses the current function until my_coro() completes. B. It schedules my_coro() to run concurrently on the active event loop as an asyncio.Task and returns immediately. C. It compiles the coroutine to C code. D. It starts a new POSIX thread.
asyncio.create_task() packages the coroutine into a Task and registers it with the running event loop for concurrent execution, returning the Task object without blocking.2.
What happens by default in asyncio.gather(task1, task2) if task1 raises an unhandled exception? A. task2 is immediately killed by the operating system. B. gather() immediately re-raises the exception from task1, while task2 continues running in the background as an orphaned task. C. The exception is converted into a string. D. The event loop crashes.
return_exceptions=False), gather() raises the first encountered exception immediately, but does not cancel or terminate the remaining sibling tasks.3.
What major concurrency advancement does Python 3.11's asyncio.TaskGroup provide over older task management patterns? A. It disables the GIL across multiple threads. B. It enforces Structured Concurrency: if any child task in the group fails, all other active tasks in the group are automatically cancelled, eliminating leaked orphan tasks. C. It converts asynchronous code to GPU shaders. D. It eliminates the need for await.
asyncio.TaskGroup introduces structured concurrency, ensuring that all tasks spawned within its context block are cleanly resolved, and automatically cancelling remaining siblings if an exception occurs.4.
When a task is cancelled via task.cancel(), what exception is raised inside the coroutine at its current await suspension point? A. StopIteration B. asyncio.CancelledError C. KeyboardInterrupt D. TimeoutError
.cancel() on an asyncio.Task injects asyncio.CancelledError into the coroutine at its active await expression.5.
What happens if a coroutine catches asyncio.CancelledError in a try...except block and suppresses it without re-raising? A. The task refuses to cancel and continues running, violating cancellation protocols. B. The event loop shuts down. C. A SyntaxError is logged. D. Python reboots the machine.
CancelledError is caught and not re-raised, the task does not acknowledge cancellation and continues executing, which breaks structured concurrency cancellation semantics.Project: Async Web Scraper
Continue learning with hands-on practice, examples, and exercises in the upcoming topic.
Related Lessons
| Previous Lesson | Next Lesson |
|---|---|
| Async and Await Syntax | Project: Async Web Scraper |
Practice Quiz
Test your understanding of this lesson with 5 questions. Each question has one correct answer.