Перейти к содержанию
Языки

TaskGroup и gather в Python asyncio: ошибки и отмена на практике

Две конкурентные проверки обслуживают запрос: проверка остатков падает, пока расчёт цены ещё выполняется. Нужно ли продолжать расчёт или вся операция должна завершиться ошибкой? TaskGroup и gather ведут себя по-разному. Пример использует события вместо догадок о времени и показывает, кто управляет незавершённой задачей. Требуется Python 3.11 или новее.

Автор: Опубликовано

4 мин чтенияРедакционный разборОбновлено
  • Python
  • asyncio
  • Конкурентность
  • Бэкенд-собеседования
Главная мысль

Выберите политику ошибок до выбора API. TaskGroup ограничивает область связанных задач; gather позволяет собрать независимые результаты. Отмена требует согласованной очистки и не откатывает внешние побочные эффекты.

Решите, что означает ошибка соседней задачи

Для полной цены продолжение расчёта после ошибки проверки остатков может быть бесполезным. Для независимых виджетов частичный результат всё ещё полезен. Сначала опишите нужное продукту поведение, затем выберите механизм. Таблица относится к обычной ошибке дочерней задачи, например ValueError; отмена дочерней задачи и отмена всей операции — отдельные случаи.

Политики конкурентной работы
ВариантОшибка дочерней задачиКогда подходит
TaskGroupОтменяет соседей в группеСвязанные подзадачи
gather()Передаёт; соседи работаютЯвное продолжение
gather(return_exceptions=True)Собирает результат или ошибкуЧастичный результат

Запустите воспроизводимую ошибку соседней задачи

Сохраните код в один Python-файл и запустите. slow сигнализирует о начале работы, затем ждёт разрешающего события. fail не может выбросить исключение до этого сигнала. TaskGroup отменяет slow и ждёт очистку до обработки сгруппированного ValueError. В варианте gather мы намеренно разрешаем продолжение и дожидаемся работающей задачи.

Ожидаемые строки: slow cleanup; group caught; group cancelled: True; gather cancelled: False; slow cleanup; slow result. Два сообщения об очистке имеют разные причины: отмена в группе и обычное завершение в gather. События позволяют наблюдать разницу без зависимости от случайного победителя гонки задержек.

import asyncio

async def slow(started, release):
    try:
        started.set()
        await release.wait()
        return "slow result"
    finally:
        print("slow cleanup")

async def fail(started):
    await started.wait()
    raise ValueError("stock")

async def taskgroup_demo():
    started, release = asyncio.Event(), asyncio.Event()
    try:
        async with asyncio.TaskGroup() as group:
            sibling = group.create_task(slow(started, release))
            group.create_task(fail(started))
    except* ValueError:
        print("group caught")
    print("group cancelled:", sibling.cancelled())

async def gather_demo():
    started, release = asyncio.Event(), asyncio.Event()
    sibling = asyncio.create_task(slow(started, release))
    try:
        await asyncio.gather(sibling, fail(started))
    except ValueError:
        print("gather cancelled:", sibling.cancelled())
    release.set()
    print(await sibling)

async def main():
    await taskgroup_demo()
    await gather_demo()

asyncio.run(main())

Не путайте передачу ошибки и отмену

Обычный gather передаёт первую обычную ошибку, не отменяя соседей. Вызов cancel у gather, уже завершённого передачей ошибки, их не останавливает. Напротив, отмена незавершённого gather запрашивает отмену незавершённых дочерних задач. При диагностике сервиса различайте эти две последовательности.

Завершение asyncio.run сразу после обработки ошибки может отменить оставшиеся задачи при остановке цикла, скрыв поведение gather. Здесь мы явно ждём соседа до возврата из main. Если продолжение работы предусмотрено в настоящем коде, храните ссылки, наблюдайте результаты и определите, кто управляет работой после ошибки запроса.

Рассматривайте собранные ошибки как решения, а не данные

При return_exceptions=True gather дожидается результатов и возвращает ошибки вместе с успешными значениями в порядке входных аргументов. Для независимых виджетов сопоставьте каждый результат виджету и выберите данные, сообщение об ошибке или повтор. Не передавайте смешанный список потребителю, ожидающему только успешные ответы.

Для TaskGroup except* ValueError обрабатывает подходящие ошибки внутри группы; другие ошибки остаются необработанными. Используйте это, когда определена реакция на соответствующий тип ошибок. Запись в журнал с подавлением всех групповых ошибок сделала бы незавершённую операцию похожей на успешную.

Согласуйте отмену с очисткой ресурсов

Освобождайте ресурсы в finally или подходящем контекстном менеджере. Если явно ловите CancelledError для записи события или очистки, обычно выбрасывайте его повторно. Он наследуется от BaseException, поэтому except Exception его не ловит. Подавление отмены может нарушить политику завершения внешней операции.

Отмена — запрос на остановку, который задача обрабатывает при следующей возможности. Корутина, блокирующая цикл событий, мешает своевременному выполнению. Отмена также не откатывает принятый платёж или зафиксированную запись: им нужны отдельные транзакции, идемпотентность или компенсации. TaskGroup управляет задачами, созданными через группу, а не произвольными отсоединёнными задачами извне.

Упражнение с тремя сценариями ошибок

Сначала предскажите вывод без запуска. Затем добавьте return_exceptions=True и перенесите release.set() перед await asyncio.gather(...), чтобы ожидающий события сосед мог завершиться; исследуйте обе записи результата. Наконец, создайте незавершённый gather из двух дочерних задач, ожидающих разрешающих событий, отмените gather, дождитесь его CancelledError и подтвердите очистку у обеих задач.

Объясните, какое поведение подходит полной цене, а какое независимым виджетам. В обзоре вопросов Python повторите корутины и задачи, затем обозначьте границу эксперимента: он проверяет управление задачами и очистку, а не откат внешних операций.

Коротко

Частые вопросы

Отменяет ли gather другие задачи при ошибке одной из них?

Обычная ошибка при стандартном gather передаётся вызывающему коду, а соседи продолжают работу. Отмена незавершённого gather — другой случай: она запрашивает отмену его незавершённых дочерних задач.

Когда выбирать TaskGroup?

Когда задачи принадлежат одной ограниченной операции и обычная ошибка соседа должна остановить оставшуюся работу. Определите обработку группы исключений и обеспечьте сотрудничество задач при отмене.

Может ли TaskGroup заменить gather(return_exceptions=True)?

Это разные политики. Собранные ошибки могут поддерживать намеренный частичный результат, а ошибка группы останавливает связанную работу. Выбирайте по нужному вызывающему коду результату, а не механически заменяйте API.

Источники

Источники и редакционная политика

Сведения проверены 4 октября 2026 г. Ссылки рядом с разделами указывают источники фактов и технических объяснений. Выводы, учебные сценарии и рекомендации по подготовке — редакционная работа RecallDeck.

От чтения к воспроизведению

Отрепетируйте полный цикл интервью.

RecallDeck возвращает сложные темы по расписанию и помогает удерживать в памяти язык, SQL, архитектуру и поведенческие истории.

Начать подготовку

Продолжить подготовку