Asyncio gather: отмена оставшихся задач при ошибке
Напишите решение на Python и проверьте его на странице.
Другие упражнения по этой теме: Asyncio TaskGroup и gather: задачи на ошибки и отмену корутин.
Безопасный опрос узлов
Модуль мониторинга опрашивает удалённые узлы сети, оборачивая корутины в задачи и запуская их параллельно через asyncio.gather(*tasks, return_exceptions=False). В штатной ситуации диспетчер получает упорядоченный список ответов, а признак сбоя остаётся пустым — None возвращается и в том случае, если исходный список задач изначально был пуст.
Однако опрос может сорваться. Причиной первого сбоя бывает как обычное исключение класса Exception, так и внутренняя самоотмена одной из дочерних задач через asyncio.CancelledError. При первом же таком исключении диспетчер перехватывает исходный объект сбоя и не оставляет брошенных фоновых процессов: он находит все ещё не завершившиеся задачи, вызывает для каждой cancel() и гарантированно дожидается их окончательной остановки через gather(*unfinished, return_exceptions=True).
В случае аварии частичные данные узлов отбрасываются, сменяясь значением None. Вместо них диспетчер сохраняет точный объект пойманного первого исключения и подсчитывает, сколько задач набора после полной остановки действительно перешло в состояние task.cancelled() is True. Реализуйте функцию безопасного опроса, возвращающую результаты штатного сбора либо исходную ошибку с точным числом подтверждённых отмен.
Что уже дано
В заготовке объявлена асинхронная функция safe_gather(factories). В окружении уже доступна синхронная вспомогательная функция coordinator_cancel_requested(), которая возвращает True, если у текущей задачи диспетчера есть активный внешний запрос на отмену (task.cancelling() > 0). Её не требуется реализовывать самому.
Что нужно сделать
Реализуйте асинхронную функцию safe_gather(factories).
Функция принимает список factories, содержащий от 0 до 50 вызываемых фабрик без аргументов. Каждый вызов фабрики возвращает новую завершаемую корутину, которая возвращает значение, выбрасывает Exception или внутри себя отменяется через asyncio.CancelledError.
Функция возвращает кортеж из трёх элементов: (results, error, cancelled_count).
Правила выполнения:
- Вызовите каждую фабрику из списка ровно один раз и оберните каждую полученную корутину в реальную задачу
asyncio.Task. - Запустите первичное ожидание задач через
asyncio.gather(*tasks, return_exceptions=False). - При полном успехе (включая пустой список
factories) верните кортеж(ordered_values, None, 0), сохранив значения в порядке исходных фабрик. - При первом обычном сбое (
Exception) или внутренней самоотмене дочерней задачи (asyncio.CancelledErrorпри условии, чтоcoordinator_cancel_requested()возвращаетFalse): - сохраните точный объект первого пойманного исключения;
- найдите все задачи исходного набора, которые ещё не завершились (
not task.done()), вызовите для нихcancel()и дождитесь их завершения черезawait asyncio.gather(*unfinished, return_exceptions=True); - верните кортеж
(None, original_first_caught_error, confirmed_cancelled_count); - частичные результаты успешных задач не возвращаются, а последующие ошибки очистки не заменяют первый пойманный объект сбоя;
confirmed_cancelled_count— точное число задач исходного набора, у которыхtask.cancelled() is True(задача, перехватившая отмену и вернувшая значение или обычную ошибку, не считается отменённой);- все созданные задачи должны полностью завершиться до выхода из функции.
- Если отмена пришла извне самому координатору (
coordinator_cancel_requested()равенTrue),CancelledErrorраспространяется дальше как отмена координатора, не превращаясь в отчёт о внутреннем дочернем сбое.
Ввод и вывод
Стандартные потоки ввода stdin и вывода stdout в решении не используются: считывать ввод и вызывать print() не нужно. После выполнения кода учащегося проверяющая система считывает из пространства имён модуля функцию верхнего уровня safe_gather и вызывает её с проверочными аргументами.
Пример использования
async def ok():
await asyncio.sleep(0)
return 0
async def empty_value():
return None
async def example():
values, error, cancelled = await safe_gather([ok, empty_value])
print(values)
print(error is None)
print(cancelled)
asyncio.run(example())
Вывод:
[0, None]
True
0
Без регистрации. Гостевая проверка не сохраняет прогресс. Для сохранения перейдите к уроку.
Отправьте решение, чтобы увидеть результаты тестов.
Связанные задачи
- Asyncio TaskGroup и gather: что происходит при ошибке корутины
- Asyncio TaskGroup: сбор результатов с отменёнными задачами
- Asyncio TaskGroup: обработка ошибок параллельного опроса
- Asyncio TaskGroup: двухфазная проверка и публикация конфигурации
- Asyncio gather и TaskGroup: соберите исключения конкурентных задач
- Удаление ключа из ChainMap в Python: проверка поведения del
