Вызов actor-изолированного async-метода после await продолжится на том же потоке?
Нет. После await actor-изолированный метод продолжится на исполнительном контексте соответствующего actor, но не обязан использовать тот же поток ОС. Поток может измениться; гарантия относится к изоляции состояния actor, а не к идентичности потока.
Модель async/await появилась, чтобы отделить логическое выполнение операции от конкретного потока и не заставлять разработчика вручную управлять потоками. Actors решают другую задачу: сериализуют доступ к своему изменяемому состоянию и тем самым помогают предотвращать гонки данных.
Такое разделение важно для масштабирования: система конкурентности может выбирать потоки эффективнее, чем код, жёстко привязанный к конкретному потоку. Поэтому Swift намеренно предоставляет гарантии изоляции и порядка доступа, но обычно не гарантирует сохранение потока после приостановки.
Разработчик может ошибочно считать, что actor подобен закреплённому за одним потоком объекту. Тогда в actor-изолированном методе начинают хранить потокозависимое состояние, использовать Thread.current как идентификатор владения или предполагать, что код после await выполняется в том же окружении.
Это приводит к некорректным проверкам, трудно воспроизводимым ошибкам и нарушению контрактов сторонних библиотек, которым действительно требуется определённый поток. Сам actor при этом продолжает защищать своё состояние, но не исправляет неверные предположения о потоках.
Обычный actor имеет исполнительный контекст actor. Когда actor-изолированный метод выполняется, доступ к его изолированному состоянию контролируется этим контекстом. При достижении await текущая задача может приостановиться, освобождая исполнитель для других задач.
После завершения ожидаемой операции продолжение задачи снова будет запланировано с соблюдением изоляции actor. Однако это продолжение может выполнить другой поток ОС. Таким образом, правильный инвариант звучит так: «доступ к состоянию происходит через actor», а не «доступ всегда происходит из одного потока».
await не означает обязательную смену потока: операция может завершиться без фактической приостановки. Но код обязан быть корректным при возможной приостановке и последующем выполнении на другом потоке.
Если нужен конкретный поток, его следует обеспечивать отдельным механизмом: например, MainActor для главного исполнительного контекста или API библиотеки, явно предоставляющим потоковую гарантию. Нельзя получать такую гарантию лишь из того, что метод принадлежит обычному actor.
Важно также не путать actor с mutex. Mutex защищает критическую секцию независимо от async-приостановок, а actor организует сериализованную обработку изолированных обращений. Ни один из этих механизмов сам по себе не делает произвольный внешний API безопасным для вызова с любого потока.
Сервис загрузки изображений оформлен как обычный actor. В его методе после сетевого await разработчик проверяет текущий поток и передаёт результат в библиотеку, которая якобы требует тот же поток, с которого началась загрузка.
Вариант с сохранением идентификатора потока до await ненадёжен: продолжение может быть запланировано на другом потоке. Вариант с ручной блокировкой тоже не решает проблему потоковой привязки и может создать блокировки вокруг асинхронного кода.
Правильное решение — оставить состояние кэша изолированным actor, а вызов потокозависимой библиотеки явно перенести в требуемый контекст, например через MainActor, если библиотека действительно требует главный поток. В результате actor отвечает за безопасность данных, а отдельный исполнительный контекст — за потоковый контракт внешнего API.
Означает ли продолжение после await обязательную смену потока?
Нет. await обозначает потенциальную приостановку, а не обязательную миграцию на другой поток. Ожидаемая операция может уже завершиться, и задача продолжит выполняться без заметной смены потока. Код, однако, должен быть корректен и в случае реальной приостановки.
Сохраняется ли actor-изоляция после await, если поток изменился?
Да, если выполнение остаётся в actor-изолированном методе. После возобновления доступ к состоянию снова подчиняется исполнительному контексту actor, независимо от того, какой поток ОС фактически выполняет код. Изменение потока не означает потерю изоляции.
Можно ли использовать номер потока как признак того, что код всё ещё принадлежит actor?
Нет. Поток не является идентификатором actor и может меняться между двумя участками одного async-метода. Принадлежность к actor определяется правилами изоляции и планированием через его исполнительный контекст, а не значением Thread.current.