Добавление дочерней задачи в TaskGroup означает, что её тело начнёт выполняться немедленно?
Нет. Вызов addTask лишь создаёт дочернюю задачу и передаёт её планировщику; Swift не гарантирует, что её тело начнёт выполняться до возврата из addTask или раньше следующего действия родительской задачи. Задача может начать выполнение сразу, позже или быть вытеснена другой задачей.
TaskGroup появился как часть структурированной конкурентности Swift, чтобы запускать динамическое число операций без ручного управления их жизненным циклом. Исходная проблема заключалась в том, что неструктурированные задачи легко теряются, переживают владельца и усложняют обработку ошибок и отмены.
Структурированная модель отделяет момент создания задачи от гарантий её жизненного цикла. Группа обязуется дождаться своих дочерних задач при выходе из области, но не обязуется выполнять их немедленно или в определённом порядке.
Нельзя использовать addTask как барьер синхронизации или как сигнал о том, что дочерняя операция уже начала работу. Если родитель сразу после добавления проверяет общий результат, ожидает побочный эффект или рассчитывает на конкретный порядок вывода, программа становится зависимой от планировщика.
Это может привести к гонкам на уровне логики: данные ещё не подготовлены, хотя родитель уже продолжил выполнение. При этом сама модель памяти Swift не становится небезопасной автоматически — проблема состоит в неверном предположении о расписании задач.
addTask создаёт дочернюю задачу в пределах группы и делает её доступной планировщику. Планировщик выбирает момент выполнения с учётом доступности исполнителей, приоритета, текущей загрузки и других внутренних факторов; публичного контракта о немедленном запуске нет.
Порядок двух сообщений не следует считать фиксированным. Чтобы получить результат дочерней задачи, нужно явно потреблять результаты группы через next() или for await; для завершения всех дочерних задач достаточно выйти из области группы, поскольку это предусмотрено её структурированным жизненным циклом.
Группа также не обещает порядок начала задач и порядок завершения задач. Если порядок является частью алгоритма, его нужно выразить через явную зависимость: сначала дождаться одного результата, затем создать следующую задачу, либо использовать отдельный синхронизирующий примитив.
Важно отличать отсутствие гарантии немедленного старта от ленивости. Дочерняя задача не является ленивым вычислением, которое начнётся только при чтении результата: после добавления она может выполняться независимо от того, начал ли родитель перебирать результаты.
Сервис обрабатывает список файлов и добавляет по одной задаче в TaskGroup. После каждого addTask разработчик обновляет счётчик «обработанных файлов», предполагая, что дочерняя задача уже начала работу. На загруженном устройстве счётчик обновляется раньше фактического чтения файла, поэтому интерфейс показывает некорректный прогресс.
Вариант с ожиданием фиксированной задержки ненадёжен: задержка не создаёт зависимости и не гарантирует запуск задачи. Вариант с общей блокировкой вокруг счётчика защищает данные, но не исправляет неверное определение момента прогресса.
Корректное решение — считать файл начатым только внутри тела дочерней задачи, а завершённым — после получения её результата родительской задачей. Так сохраняется структурированность, отсутствуют предположения о планировщике, а обновление прогресса привязывается к реальному событию.
withTaskGroup сразу после добавления дочерней задачи?Нет. Выход из области группы не отменяет обязанность дождаться дочерних задач: группа структурированно ожидает их завершения. Для throwing-группы дополнительно учитываются ошибки и отмена оставшихся дочерних задач согласно используемому API и обработке результата.
addTask порядок выполнения дочерних задач?Нет. Порядок добавления не является контрактом порядка запуска или завершения. Если результаты читаются через for await, они обычно поступают по мере завершения дочерних задач, а не в порядке добавления; для исходного порядка нужно сохранить идентификатор или индекс и затем упорядочить результаты.
Не следует полагаться на последовательность вызовов addTask. Нужно явно ожидать результат предыдущей операции перед созданием следующей задачи либо выразить зависимость через последовательную часть алгоритма. Если операции действительно независимы, лучше оставить их конкурентными и синхронизировать только тот результат, который требует согласованного порядка.