Оцените безопасность передачи дескриптора Task между конкурентными задачами без дополнительной блокировки.
Дескриптор Task можно безопасно передавать и совместно использовать между конкурентными задачами без собственного lock. Он представляет управляемую задачу, а не прямой доступ к её изменяемому внутреннему состоянию. Несколько задач могут ожидать один результат через value, а отмену можно запрашивать через общий дескриптор.
Однако это не делает произвольный результат автоматически безопасным для передачи. При пересечении изолированных областей тип результата должен удовлетворять требованиям Sendable, если этого требует контекст компиляции.
В модели структурированной конкурентности Swift задачи образуют иерархию, но иногда результат нужно передать в код, который не является непосредственным родителем задачи. Для этого используются дескрипторы Task: они позволяют сохранить ссылку на выполняющуюся или уже завершённую операцию.
Без безопасного дескриптора разработчикам пришлось бы самостоятельно синхронизировать состояние задачи, ожидание результата и уведомление о завершении. Swift предоставляет эти гарантии внутри самого типа Task.
Одна задача может запустить операцию, а несколько других — получить её результат. Наивная реализация могла бы хранить результат в общей изменяемой переменной и защищать её mutex-ом, что создаёт риск гонок, взаимных блокировок и ошибок при публикации состояния.
Отдельный риск связан с тем, что безопасное использование дескриптора не означает безопасность данных, захваченных самой задачей. Если задача обращается к общему изменяемому объекту без изоляции, дескриптор не исправляет эту гонку.
Дескриптор Task является безопасным объектом управления задачей. Операция чтения value асинхронно ожидает завершения и возвращает один и тот же сохранённый результат каждому ожидающему. Повторное ожидание не запускает работу заново.
В примере нет общего изменяемого счётчика: обе задачи обращаются к безопасному дескриптору и получают результат завершившейся операции. Само выполнение задачи при этом остаётся конкурентным, а порядок завершения не следует выводить из порядка создания ожидающих операций.
Дескриптор также поддерживает кооперативную отмену через cancel(). Это только устанавливает признак отмены; немедленная остановка не гарантируется, поскольку тело задачи должно проверять отмену или вызывать API, реагирующие на неё.
Важно разделять безопасность дескриптора и безопасность результата. Передача Task между изолированными областями допустима, но получение несамопередаваемого результата через границу конкурентности может быть запрещено проверками Swift или потребовать иной архитектуры.
Сервис изображений запускает дорогое декодирование один раз, а экран и модуль аналитики независимо ожидают его результат. Вариант с двумя отдельными запусками дублирует вычисление и увеличивает нагрузку. Вариант с общей изменяемой переменной требует ручной синхронизации и усложняет обработку ошибок.
Выбранный вариант — сохранить дескриптор одной задачи и передать его потребителям. Каждый потребитель ожидает value, а владелец дескриптора отменяет задачу, когда результат больше не нужен. Это устраняет дублирование работы, но требует явно определить владельца отмены и убедиться, что результат можно безопасно пересечь с нужными изолированными областями.
Запускается ли операция заново при повторном чтении Task.value?
Нет. Дескриптор относится к одной конкретной задаче. Повторное чтение ожидает ту же операцию и получает сохранённый результат или ту же ошибку, если задача завершилась неуспешно.
Делает ли общий дескриптор безопасным захваченное задачей состояние?
Нет. Безопасен способ управления задачей, но не произвольные объекты, к которым обращается её тело. Общий изменяемый класс по-прежнему должен быть защищён actor-изоляцией, другим механизмом синхронизации или корректной моделью владения.
Гарантирует ли передача дескриптора, что результат можно передать куда угодно?
Нет. Дескриптор и его результат — разные вопросы. Если результат пересекает границу между конкурентными задачами или акторами, его тип и захваченные значения должны соответствовать требованиям Sendable; иначе безопаснее изменить тип результата или вернуть данные через изолированный владелец.