Допустимо ли считать вызов nonisolated-метода actor сериализованным самим actor?
Нет. nonisolated-метод не выполняется в изоляции экземпляра actor, поэтому его вызовы не сериализуются executor’ом этого actor и могут выполняться одновременно. Такой метод обязан быть безопасным независимо от actor-изоляции и не может напрямую обращаться к изолированному изменяемому состоянию actor.
Акторы появились в Swift для структурирования конкурентного доступа к изменяемому состоянию без ручных блокировок. По умолчанию их изолированные методы выполняются через executor actor, что исключает одновременное выполнение изолированных участков для одного экземпляра.
Однако некоторым операциям не нужен доступ к состоянию actor: например, чистому вычислению или обработке заранее переданных данных. Для таких случаев существует nonisolated, позволяющий явно исключить метод из actor-изоляции и не платить за ненужную сериализацию.
Название метода как члена actor может создать ложное ощущение, что любой его вызов защищён actor. Если nonisolated-метод обращается к общей изменяемой ссылке, глобальному состоянию или другому ресурсу без собственной синхронизации, параллельные вызовы могут привести к гонке данных.
Неверное предположение особенно опасно для асинхронного nonisolated-метода: после await он также не обязан возвращаться к executor’у исходного actor. Его продолжения должны рассматриваться как обычный конкурентный код.
nonisolated снимает привязку метода к изоляции конкретного экземпляра actor. Поэтому синхронный такой метод обычно вызывается без await, а его выполнение не проходит через очередь изолированных обращений actor.
Метод normalize не читает entries, поэтому ему не нужна actor-изоляция. Несколько задач могут вызвать его параллельно; actor не будет превращать эти вызовы в последовательную очередь.
Из nonisolated-метода нельзя напрямую читать или изменять изолированное изменяемое свойство actor. Чтобы обработать такое состояние безопасно, нужно оставить метод изолированным либо сначала получить из actor безопасный снимок данных, а затем передать этот снимок в независимую функцию.
Практический компромисс таков: изолированный метод даёт сериализацию и безопасный доступ к состоянию, но может стать точкой конкуренции. nonisolated улучшает параллелизм и уменьшает задержки, однако переносит ответственность за безопасность на реализацию метода и используемые им ресурсы.
В сервисе actor хранит настройки, а метод форматирования ответа объявлен nonisolated. Команда считала, что вызовы форматирования автоматически проходят последовательно через actor, и внутри метода использовала общий изменяемый кэш без синхронизации. При нагрузке появились повреждённые записи и непредсказуемые результаты.
Рассматривались два варианта. Возврат метода в actor-изоляцию устранял гонку, но заставлял все запросы конкурировать за один executor. Внешняя блокировка сохраняла nonisolated, однако добавляла ручное управление синхронизацией и риск ошибочного использования.
Выбрали разделение ответственности: actor формировал неизменяемый Sendable-снимок настроек, а nonisolated-функция форматировала только этот снимок и локальные значения. В результате состояние actor осталось защищённым, форматирование стало параллельным, а общий изменяемый кэш был удалён.
Нет. Сам факт принадлежности метода типу actor не задаёт ему actor-изоляцию после объявления nonisolated. Планирование происходит по общим правилам Swift Concurrency, поэтому нельзя использовать такой метод как способ принудительно выполнить работу на executor actor.
Только если это разрешено правилами изоляции и тип значения безопасен для передачи за пределы actor. Неизменяемость сама по себе не делает произвольный ссылочный объект безопасным: let может указывать на объект с изменяемым внутренним состоянием. Поэтому важно оценивать не только возможность записи в свойство, но и безопасность самого значения.
Нет. async позволяет приостанавливать задачу, но не добавляет синхронизацию. Асинхронный nonisolated-метод всё равно не получает автоматически доступ к изолированному состоянию actor и не сериализуется его executor’ом; для общего изменяемого состояния нужна actor-изоляция или отдельный корректный механизм синхронизации.