Skip to content

Commit 76942ed

Browse files
committed
Indicate how to cancel a task group
1 parent d343f97 commit 76942ed

File tree

1 file changed

+67
-0
lines changed

1 file changed

+67
-0
lines changed

Doc/library/asyncio-task.rst

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -414,6 +414,73 @@ reported by :meth:`asyncio.Task.cancelling`.
414414
Improved handling of simultaneous internal and external cancellations
415415
and correct preservation of cancellation counts.
416416

417+
Group Cancellation
418+
------------------
419+
420+
Cancelling an entire task group is not natively supported by the standard
421+
library but can be achieved by adding a task to the group that raises an
422+
exception and ignore that exception:
423+
424+
.. code-block:: python
425+
426+
import contextlib
427+
from asyncio import TaskGroup
428+
429+
class CancelTaskGroup(Exception):
430+
"""Exception raised to cancel a task group."""
431+
432+
@contextlib.asynccontextmanager
433+
async def CancellableTaskGroup():
434+
"""A cancellable task group."""
435+
try:
436+
async with TaskGroup() as group:
437+
yield group
438+
except* CancelTaskGroup:
439+
print('task group was cancelled')
440+
441+
async def cancel_group(cancellable_task_group):
442+
"""Create a task that will cancel the group it belongs to."""
443+
async def factory():
444+
raise CancelTaskGroup(cancellable_task_group)
445+
await cancellable_task_group.create_task(factory())
446+
447+
The above helper functions can be used as follows:
448+
449+
.. code-block:: python
450+
451+
import asyncio
452+
453+
async def job(task_id, sleep_time, group=None):
454+
print(f"Task {task_id}: start")
455+
await asyncio.sleep(sleep_time)
456+
if group is not None:
457+
print(f"Task {task_id}: cancelling group")
458+
await cancel_group(group)
459+
print(f"Task {task_id}: done")
460+
461+
async def main():
462+
async with CancellableTaskGroup() as group:
463+
# task 1 is cancelled before being done
464+
group.create_task(job(1, 2, None))
465+
# task 2 is responsible for cancelling the entire group
466+
group.create_task(job(2, 1, group))
467+
# task 3 completes normally
468+
group.create_task(job(3, 0, None))
469+
470+
asyncio.run(main())
471+
472+
Expected output (note that "Task 2: done" is not printed since task 2 is
473+
itself cancelled before completion):
474+
475+
.. code-block:: text
476+
477+
Task 1: start
478+
Task 2: start
479+
Task 3: start
480+
Task 3: done
481+
Task 2: cancelling group
482+
task group was cancelled
483+
417484
Sleeping
418485
========
419486

0 commit comments

Comments
 (0)