Что такое serial executor в Swift и как он гарантирует выполнение кода в выделенной очереди?
Что проверяют
Разбираетесь ли вы в модели конкурентности Swift или пересказываете очереди другими словами. Serial executor это часть модели акторов, и вопрос проверяет главное: что именно гарантируется, взаимное исключение или поток.
Как отвечать
Executor это объект, умеющий принять задание и выполнить его. Serial executor обещает, что два задания не выполняются одновременно. Именно он стоит за актором: у актора есть unownedExecutor, и вызов изолированного метода ставит задание в него.
final class QueueExecutor: SerialExecutor {
private let queue = DispatchQueue(label: "storage")
func enqueue(_ job: consuming ExecutorJob) {
let job = UnownedJob(job)
let executor = asUnownedSerialExecutor()
queue.async { job.runSynchronously(on: executor) }
}
}Актор подключает такой исполнитель, объявив nonisolated свойство unownedExecutor. Что стоит проговорить дальше.
Гарантия это взаимное исключение, а не поток. Задания актора выполняет кооперативный пул, и два подряд идущих вызова могут исполниться на разных потоках. Исключение это главный актор: его исполнитель привязан к главному потоку.
Порядок не гарантирован. На каждом await актор реентерабелен: он отпускает исполнителя, и до возобновления внутрь может зайти другой вызов. Инварианты проверяют после await, а не до него.
Свой исполнитель нужен, когда изоляцию требуется свести к чужой очереди: библиотека с требованиями к потоку, постепенная миграция кода. Блокировать исполнитель нельзя: сон и семафоры съедают поток пула и приводят к взаимной блокировке.
Где спотыкаются
Отвечают про последовательную очередь и отправку задач в неё. Это соседний механизм: спрашивают про акторы, где очередь в лучшем случае лежит внутри реализации исполнителя.
Обещают выполнение заданий строго в порядке добавления. Для последовательной очереди это так, для актора нет из-за реентерабельности, и на этом ломаются счётчики и логика «побеждает последний ответ».
Приравнивают изоляцию актора к потоку и тащат внутрь код с локальными для потока значениями. Помогают явная привязка к главному актору и assumeIsolated, а не вера в то, что у актора есть свой поток.