В throwing task group одна дочерняя задача завершилась ошибкой, но родитель ещё не ожидает результат группы: что происходит с остальными дочерними задачами?
Ошибка дочерней задачи не обязана немедленно остановить остальные задачи. Пока родитель не обнаружил эту ошибку через ожидание результата группы и не вышел из области группы с ошибкой, остальные дочерние задачи могут продолжать выполнение. После выхода из throwing task group с ошибкой незавершённые дочерние задачи отменяются, но их остановка остаётся кооперативной.
Структурированная конкурентность появилась как способ связать жизненный цикл дочерних задач с областью видимости родительской задачи. Это решает проблему «осиротевших» задач, для которых непонятно, кто отвечает за ожидание результата, обработку ошибки и завершение.
Task group дополняет эту модель динамическим созданием дочерних задач. В отличие от независимой Task, такие задачи принадлежат группе: группа не считается завершённой, пока её дочерние задачи не завершены или не отменены.
Предположим, группа запускает несколько запросов, а один из них завершается ошибкой. Ошибочно считать, что сам факт ошибки синхронно прервал все остальные запросы: они могут продолжать расходовать ресурсы и выполнять побочные действия.
Также ошибка не означает принудительное завершение. Отмена в Swift кооперативна: задача должна проверить состояние отмены или вызвать отменяемую операцию, которая сама отреагирует на отмену. Поэтому родитель должен корректно дождаться завершения группы и не оставлять незавершённую работу без контроля.
В throwing task group ошибка дочерней задачи становится доступна родителю, когда родитель ожидает результат группы, например через получение следующего результата. Если это ожидание выбрасывает ошибку и ошибка выходит из тела группы, группа отменяет оставшиеся дочерние задачи.
Отмена распространяется на дочерние задачи, но не является убийством потока. Задача может некоторое время продолжать работу, если выполняет нечувствительный к отмене синхронный код, игнорирует Task.isCancelled или использует операцию, которая не реагирует на отмену.
Группа всё равно дожидается завершения своих дочерних задач перед выходом из области. Это важная гарантия структурированной конкурентности: после выхода из withThrowingTaskGroup дочерние задачи не продолжают жить независимо от родителя.
Если родитель обработал ошибку внутри тела группы и вернулся нормально, автоматического распространения этой ошибки наружу не будет. В таком случае решение о дальнейшей отмене и сборе результатов принимает код тела группы.
Минимальная схема поведения:
После получения ошибки из group.next() тело группы завершается с ошибкой, поэтому оставшаяся задача отменяется. Однако Task.sleep лишь реагирует на отмену и завершает ожидание ошибкой; произвольный код внутри дочерней задачи может потребовать явной проверки Task.isCancelled.
Компромисс такой: ранняя отмена экономит ресурсы и ускоряет отказ всей операции, но требует, чтобы дочерние задачи были корректно отменяемыми. Если отдельные результаты допустимы при частичном отказе, ошибки лучше перехватывать внутри дочерних задач или внутри тела группы, а не автоматически пробрасывать их наружу.
Сервис одновременно загружает профиль, рекомендации и рекламный блок. Рекламный запрос завершился ошибкой, тогда как профиль уже почти готов, а рекомендации выполняют тяжёлый расчёт.
Первый вариант — использовать withThrowingTaskGroup и сразу пробрасывать любую ошибку. Его плюс — простая семантика «всё или ничего» и отмена ненужной работы. Минус — временная ошибка необязательного блока отменит получение полезных данных.
Второй вариант — обернуть каждый дочерний запрос в обработку ошибки и вернуть результат с признаком неуспеха. Это позволяет показать профиль и рекомендации, но усложняет тип результата и требует явно определить, какие ошибки допустимы.
Для этого сервиса выбран второй вариант: рекламный блок не является обязательным для экрана. Ошибка обязательного запроса завершает группу с ошибкой, а необязательная ошибка превращается в отсутствие соответствующего результата. В итоге отмена применяется только там, где она действительно соответствует бизнес-смыслу операции.
Нет, нельзя формулировать это как безусловную немедленную отмену. Ошибка должна быть обнаружена родителем во время ожидания результата группы; затем выход из тела группы с ошибкой приводит к отмене оставшихся задач. Между фактическим завершением дочерней задачи с ошибкой и наблюдением этой ошибки родителем другие задачи могут продолжать работу.
Нет. Отмена кооперативна, поэтому задача может успеть записать данные, отправить сетевой запрос или изменить состояние до проверки отмены. Критические операции должны быть спроектированы так, чтобы повторный запуск и частичное выполнение были безопасными, а доступ к общему изменяемому состоянию оставался синхронизированным.
Ошибка не выйдет из withThrowingTaskGroup, потому что родитель её поглотил. Незавершённые дочерние задачи при этом всё равно остаются частью группы: перед выходом из области группа должна дождаться их завершения; если они больше не нужны, код должен явно инициировать их отмену и затем корректно обработать их завершение. Это позволяет реализовать частичный успех, но ответственность за выбранную политику обработки ошибок ложится на разработчика.