Может ли дочерняя задача изменить значение task-local переменной, видимое родительской задачей?
Нет. Дочерняя задача может временно переопределить task-local значение в своей динамической области, но это не изменит значение, видимое родительской задачей. После выхода из области переопределения восстанавливается прежнее значение.
Task-local values появились как часть модели конкурентности Swift для передачи контекста без явного протягивания параметров через каждый вызов. Типичные примеры — идентификатор запроса, трассировочный контекст или настройки логирования.
Подход решает проблему контекстных данных, но намеренно не превращает их в общее изменяемое состояние. Это помогает отличать метаданные выполнения задачи от данных, которыми конкурентные задачи должны совместно управлять через actor или другой механизм синхронизации.
Если воспринимать task-local переменную как глобальную переменную, можно ошибочно ожидать, что присваивание в дочерней задаче изменит контекст родителя. На практике переопределение действует только внутри определённой области и не является операцией записи в общий контейнер.
Неверная модель особенно опасна при логировании: дочерняя операция может установить собственный идентификатор, а родитель продолжит использовать исходный. Для реально общего изменяемого состояния task-local values не подходят.
Task-local значение доступно через динамический контекст текущей задачи. Вызов withValue создаёт вложенную область с другим значением; после завершения замыкания прежнее значение автоматически восстанавливается.
Внутри области будет напечатано child, а после неё — none, если внешнее значение не было задано. Даже если тело завершится ошибкой или будет отменено, область не превращается в постоянное изменение родительского контекста.
Важно отличать замену значения от изменения объекта, на который оно ссылается. Если task-local содержит ссылочный объект, дочерняя задача может изменить сам объект, и это изменение будет общим; task-local механизм не клонирует ссылочные значения и не добавляет им потокобезопасность.
Значения task-local наследуются дочерними задачами в рамках обычного структурированного контекста, но это наследование создаёт контекстное значение, а не общий изменяемый слот. Для совместного состояния следует использовать actor, защищённую блокировку или другой явно выбранный механизм синхронизации.
Сервис обрабатывает HTTP-запрос и помещает его идентификатор в task-local контекст, чтобы вложенные функции автоматически добавляли его в логи. Дочерняя задача временно переопределяет идентификатор для отдельной подоперации, не влияя на логи родительской операции.
Вариант с глобальной переменной проще, но небезопасен: параллельные запросы будут перетирать контекст друг друга. Вариант с передачей идентификатора явным параметром безопаснее и прозрачнее, но требует менять большое количество сигнатур.
Выбран task-local вариант, потому что идентификатор является контекстом выполнения, а не общим состоянием. Если же подоперации должны совместно изменять счётчик или накопитель, используется actor, поскольку task-local values не обеспечивают атомарность и синхронизацию доступа.
Что произойдёт, если дочерняя задача вызовет withValue и затем создаст ещё одну дочернюю задачу?
Вложенная задача обычно унаследует значение из текущей области, то есть увидит временное переопределение. Это наследование распространяется вниз по динамическому контексту, но не обратно к родителю. После выхода внешней области родительское значение останется прежним.
Является ли task-local значение копией объекта, если переменная хранит ссылочный тип?
Нет. Копируется ссылка, а не объект. Поэтому изменение внутреннего состояния общего ссылочного объекта может привести к гонке данных; task-local область изолирует привязку значения к контексту, но не защищает объект, на который это значение указывает.
Можно ли использовать task-local переменную для накопления результата несколькими конкурентными задачами?
Нет, это неподходящий механизм. Каждая задача получает собственный контекст, а изменения временного значения не образуют безопасный общий аккумулятор и не объединяются автоматически. Для накопления результата нужно явно координировать задачи, например через actor, а затем получить итоговое значение.