Skip to content

Commit

Permalink
add Python asyncio.TaskGroup
Browse files Browse the repository at this point in the history
  • Loading branch information
copdips committed Jan 13, 2024
1 parent d379ea9 commit 927b270
Showing 1 changed file with 24 additions and 15 deletions.
39 changes: 24 additions & 15 deletions docs/posts/2023/2023-09-14-python-asyncio.md
Original file line number Diff line number Diff line change
Expand Up @@ -258,27 +258,36 @@ print(time.time() - start)
2.0026962757110596
```

## wait vs gather
## wait vs gather vs TaskGroup

- [`wait`](https://docs.python.org/3/library/asyncio-task.html#asyncio.wait) is a low-level api, [`gather`](https://docs.python.org/3/library/asyncio-task.html#asyncio.gather) is a high-level api.
- `wait` has more options than `gather`:
- `async def wait(fs, *, loop=None, timeout=None, return_when=ALL_COMPLETED):`
- `def gather(*coros_or_futures, loop=None, return_exceptions=False):`
- `async def wait(fs, *, loop=None, timeout=None, return_when=ALL_COMPLETED):`
- `def gather(*coros_or_futures, loop=None, return_exceptions=False):`
- `wait` accepts lists of coroutines/Futures (`asyncio.wait(tasks)`), `gather` accepts each element a coroutine/Futures (`asyncio.gather(*tasks)`).
- `wait` returns two `futures` in a tuple: `(done, pending)`, it's a coroutine `async def`. To get the `wait` results: `[d.result() for d in done]`, `gather` returns the results directly, it's a standard `def`.
- `gather` can group tasks, and can also cancel groups of tasks:

```python
async def main():
group1 = asyncio.gather(f1(), f1())
group2 = asyncio.gather(f2(), f2())
group1.cancel()
# if return_exceptions=False, `asyncio.exceptions.CancelledError` will be raised,
# if return_exceptions=True, the exception will be returned in the results.
# return_exceptions default value is False
all_groups = await asyncio.gather(group1, group2, return_exceptions=True)
print(all_groups)
```
- [`TaskGroup`](https://docs.python.org/3/library/asyncio-task.html#asyncio.TaskGroup): Added in [Python 3.11](https://docs.python.org/3/whatsnew/3.11.html#asyncio), an asynchronous context manager holding a group of tasks that will wait for all of them upon exit. For new code this is ===recommended over using create_task() and gather() directly===. (Contributed by Yury Selivanov and others in gh-90908.)

```python title="gather"
async def main():
group1 = asyncio.gather(f1(), f1())
group2 = asyncio.gather(f2(), f2())
group1.cancel()
# if return_exceptions=False, `asyncio.exceptions.CancelledError` will be raised,
# if return_exceptions=True, the exception will be returned in the results.
# return_exceptions default value is False
all_groups = await asyncio.gather(group1, group2, return_exceptions=True)
print(all_groups)
```

```python title="TaskGroup since Python 3.11 to replace create_task() and gather()"
async def main():
async with asyncio.TaskGroup() as tg:
task1 = tg.create_task(some_coro(...))
task2 = tg.create_task(another_coro(...))
print(f"Both tasks have completed now: {task1.result()}, {task2.result()}")
```

- If the `wait` task is cancelled, it simply throws an CancelledError and the waited tasks remain intact. Need to call `task.cancel()` to cancel the remaining tasks. If `gather` is cancelled, all submitted awaitables (that have not completed yet) are also cancelled. <https://stackoverflow.com/a/64370162>

Expand Down

0 comments on commit 927b270

Please sign in to comment.