Ответ
Он выполняется в произвольном потоке из cooperative thread pool Swift Concurrency.
Ключевые принципы:
- Наследование контекста:
Taskнаследует актор-контекст места своего создания. Если он создан внеMainActor(в обычной синхронной функции), у него нет привязки к главному потоку. - Пул потоков: Система Swift Concurrency выбирает для выполнения тела
async-функции любой доступный поток из своего пула.
Пример:
// 1. Обычная синхронная функция, не изолированная к MainActor.
func startBackgroundWork() {
Task { // Эта задача НЕ привязана к MainActor.
// Код до `await` выполняется в потоке из пула (A).
print(Thread.current) // Не главный
await asyncOperation()
// Код после `await` может выполниться в том же или ДРУГОМ потоке из пула (B).
}
}
// 2. Async-функция, вызванная внутри задачи.
func asyncOperation() async {
// Выполняется в потоке из пула Swift Concurrency.
let result = await downloadData()
}
Как управлять потоком?
- Используйте
@MainActorдля гарантии выполнения на главном потоке. - Используйте глобальные акторы (
@GlobalActor) для собственных изолированных контекстов. Task.detachedявно создает задачу, не наследующую контекст родителя.
Ответ 18+ 🔞
Давай разберём эту хуйню про потоки в Swift Concurrency, а то народ путается, как слепой кот в сортире.
Смотри, есть у нас эта штука — Task. Так вот, главный подвох в том, что эта сука наследует контекст, где её родили. Если ты её создал в обычной, не изолированной функции, то она НЕ ПРИВЯЗАНА к главному потоку. Вообще. Ноль. Ни хуя. Она как бездомный щенок — система возьмёт её и кинет в первый попавшийся свободный поток из своего пула.
Вот смотри на этот пример, он как раз про это:
func startBackgroundWork() {
Task { // Эта задача — сирота, MainActor её не усыновлял.
// Тут код до первого `await` выполнится в каком-то потоке из пула. Допустим, в потоке A.
print(Thread.current) // И тут будет НЕ главный поток, запомни.
await asyncOperation()
// А вот после `await` система может взять и перекинуть выполнение в совершенно другой поток из пула. Например, в поток B. Никаких гарантий, ёпта!
}
}
И вот эта asyncOperation() внутри — она тоже будет болтаться по этим потокам из пула, как мартышка по лианам.
Так как же, блядь, этим управлять, если всё так пиздецово?
А вот так:
@MainActor— твой бро. Помечай им всё, что должно крутиться строго на главном потоке. UI-апдейты, например. Система тогда сама, как хитрая жопа, будет переключать контекст.- Глобальные акторы (
@GlobalActor) — если тебе нужен свой, особый контекст для изоляции, а не просто главный поток. Task.detached— это когда ты говоришь: «Знаешь что? Нахуй наследование контекста! Я сам по себе мужик!». Задача запустится независимо, без привязки к контексту родителя. Полезно, но осторожно — можно и в просак сесть.
Короче, запомни: Swift Concurrency — не про ручное управление потоками. Это про то, чтобы ты описал, где (в каком актор-контексте) должен выполняться код, а система уже сама разберётся, на каком конкретном потоке его гонять. А если не опишешь — будет гонять где попало, и потом удивляйся, почему UI лагает.