Comments 15
А точно код становится многопоточным , в Swift асинхронность = многопоточность?
Гораздо интереснее было бы рассмотреть, чем он хуже GCD, так как про лучшесть и так из каждой трубы слышно.
Приоритет тасок теперь важен, так как исполняются они не в FIFO, как в GCD. Это может сбить с толку кучу народу, кто всю жизнь писал под GCD/Combine и не трогал треды. Apple на этой важнейшей детали вообще не заострил внимание
Акторы обманчиво похожи на обычный джавный монитор на самом объекте (или, если вы из мира ios, на serial queue по заходу в метод), но на самом деле критическая секция длится лишь до suspend'а. На деле это значит, то любые isolated-методы у акторов надо делать с пониманием, что зайти в метод может много клиентов сразу, но исполняется только один за раз. Это, опять же, контринтуитивно и упоминается Apple лишь вскользь
Таски захватывают свой контекст сильно до конца исполнения, даже если написать [weak self]. В интернете об этом знают полторы статьи - в итоге память течет нещадно (но, к счастью, утечки чаще всего временные). Опять же, контринтуитивно и в разрез с устоявшимся синтаксисом остального языка
Чтобы таски тестировать в юнит тестах, их приходится открывать как private(set), так как свои Executor эппл писать не разрешила. В итоге из каждого объекта торчит лишняя переменная, а тесты ходят по другим потокам даже для возвращения мок-данных, так как нельзя заставить все идти серийно, синхронно на один тред (как с GCD делалось через собственную абстракцию над DispatchQueue).
Добавлю еще один пункт к списку:
Если мы работаем с большим количеством потокоблокирующих тасок, то GCD будет отрабатывать такие случаи быстрее чем async/await. Например, если в async варианте функции из примера про Thread Explosion заменить
Task.sleep
на просто блокирующийsleep
, то GCD вариант выполнится быстрее. Это происходит из-за того что в Cooperative Thread Pull выделяется ограниченное кол-во потоков, и при блокировке они быстро заканчиваются.
Спасибо за статью. В тексте ниже наверное опечатка и имелось в виду «Все наши очереди последовательные (serial)». А то формулировка «асинхронные» вводит в заблуждение.
«Все наши очереди синхронные, высокоприоритетная задача добавилась в очередь самой последней, следовательно ей нужно подождать пока выполнятся все предыдущие таски из background
очереди»
Новая модель работы с многопоточкой async/await абстрагирует разработчика от понятий потоков и очередей. Новая модель включает в себя Cooperative Thread Pull, который инкапсулирует всю логику по работе с потоками и очередями.
Cooperative Thread Pool, паттерн такой https://en.wikipedia.org/wiki/Thread_pool
Почему в примере с try/catch не показана обработка ошибок в сравнении с вариантом на лямбда функциях?
Насколько я понял вы говорите про функцию ниже (поправьте если я не так понял)
func loadAndProcessImage(from url: URL) async throws -> UIImage {
let image = try await loadImage(from: url)
let blurredImage = try await applyBlurFilter(to: image)
try await cacheToDisk(blurredImage)
return blurredImage
}
Тут нет обработки ошибок с помощью catch, так как подразумевается что обработка будет происходить в месте вызова данной функции (она сама по себе throws
)
Я думал над пунктами "2) Неудобная обработка ошибок" и "3) Компилятор позволяет нам совершать ошибки с замыканиями". Не совсем корректно сравнивать в таком контексте - у вас в примере с gcd функция самодостаточная, а в случае с async/await вы еще должны обернуть вызов `loadAndProcessImage` в Task и в do-catch, что добавит вложенности. Получается в async/await примере вложенность у вас не намного меньше, просто часть вызовов пропущена.
У вызовов GCD тоже можно сделать небольшой уровень вложенности чисто внутренними средствами. Например, так:
https://michaellong.medium.com/how-to-chain-api-calls-using-swift-5s-new-result-type-and-gcd-56025b51033c
Не совсем соглашусь с самодостаточностью примера с GCD по сравнению с реализацией на async/await. Ведь у клиента GCD'шной реализации функции будет тоже необходимость обработать ошибку, переданную в completion, что породит +1 ветвление и уровень вложенности (как и do/catch async await). Плюс клиент может тоже заворачивать вызовы в дополнительные DispatchQueue.main (например), аналогично с Task {} у SC реализации
Swift async/await. Чем он лучше GCD?