В чем преимущество new concurrency в Swift по сравнению с GCD
Что проверяют
Понимаете ли вы, что Swift Concurrency это не синтаксический сахар над GCD, а другая модель с проверками на уровне компилятора. Следом спрашивают про акторы и их реентерабельность, про @MainActor или про то, как вызвать старый API с колбэком из async-функции.
Как отвечать
Модель появилась в Swift 5.5. Преимущества перед GCD называйте конкретно:
- линейный код:
asyncиawaitвместо вложенных замыканий, ошибки идут через обычныйthrows, а не черезResultв колбэке, и компилятор не даст забыть вернуть результат, а completion handler легко не вызвать; - структурированная конкурентность:
async letиTaskGroupпривязывают дочерние задачи к родителю. Родитель ждёт их завершения, ошибка и отмена распространяются по дереву. В GCD группы и отмену приходится собирать руками; - отмена: кооперативная, через
Task.isCancelledиTask.checkCancellation(), и она доходит до вложенных вызовов; - изоляция данных:
actorзащищает своё состояние,@MainActorгарантирует главный поток, а проверкиSendableв режиме Swift 6 превращают гонки данных в ошибки компиляции. Очередь GCD защищает данные только по договорённости; - пул потоков: задачи выполняются на кооперативном пуле размером примерно с число ядер. В GCD блокирующие задачи на concurrent-очереди приводят к взрывному росту числа потоков.
Старые API оборачивают через withCheckedContinuation или withCheckedThrowingContinuation. GCD при этом никуда не делся и уместен для низкоуровневых вещей, но в новом коде обычно выбирают async/await.
Где спотыкаются
Говорят, что акторы убирают любые гонки. Они убирают гонки данных, но актор реентерабелен: на каждом await его состояние может измениться, и проверку, сделанную до await, нужно повторять после.
Блокируют поток пула: DispatchSemaphore.wait() или синхронное ожидание внутри async-функции. Пул маленький, и так легко подвесить всё приложение.
Считают, что Task { } сам уводит работу с главного потока. Он наследует изоляцию текущего контекста, и из @MainActor его синхронный код выполнится на главном акторе. Тяжёлую работу выносят явно, например в отдельный актор или Task.detached.