На каком потоке выполняются асинхронные задачи в Swift concurrency?
Что проверяют
Не путаете ли вы «асинхронно» с «на фоновом потоке» и понимаете ли, что после await продолжение может оказаться на другом потоке. Следом обычно спрашивают, чем Task отличается от Task.detached и почему внутри async функции нельзя вызывать Thread.sleep.
Как отвечать
Базовая модель: задачи исполняются на кооперативном пуле потоков, где число потоков ограничено числом ядер. Дальше по пунктам:
- место выполнения определяет изоляция: код, изолированный в акторе, идёт в исполнителе этого актора, а
@MainActorэто единственная настоящая гарантия главного потока; Task { }наследует контекст изоляции и приоритет от места создания, поэтому внутри@MainActorкласса он начнётся на главном акторе;Task.detached { }не наследует ни изоляцию, ни приоритет и уходит в общий пул;awaitэто точка приостановки: поток не блокируется, он отдаётся другим задачам, а продолжение может подхватить любой поток пула;- приоритет (
Task(priority: .userInitiated)) влияет на планирование, а не на выбор конкретного потока.
Важная деталь: в Swift 6.2 появился режим NonisolatedNonsendingByDefault, который меняет старое правило про nonisolated async функции. Раньше такая функция всегда уходила на глобальный пул, теперь по умолчанию она выполняется в изоляции вызывающего, а явный уход в пул просит атрибут @concurrent. Не уверены в деталях, опишите классическое поведение и оговоритесь.
Закончите практикой: поток кооперативного пула блокировать нельзя. Thread.sleep, ожидание на DispatchSemaphore и синхронные сетевые вызовы съедают потоки, которых мало, и рантайм упирается в их нехватку. Правильный аналог паузы это try await Task.sleep(for: .seconds(1)).
Где спотыкаются
Говорят, что Task создаёт новый поток. Не создаёт: он ставит работу в очередь исполнителя, число потоков от этого не растёт.
Считают, что после await управление вернулось на прежний поток, и обновляют там интерфейс без @MainActor. На главный поток возвращает изоляция, а не удача.
Проверяют Thread.isMainThread внутри акторного кода и строят на этом логику. Единица рассуждения в Swift Concurrency это актор и его исполнитель, а не поток.