Комментарии 20
К плюсам еще отнес бы, что не надо делать функции красными. А минусы перед корутинами котлина какие видите?
Вижу 2 основных минуса перед Kotlin-корутинами:
Kotlin-корутины хорошо оптимизированы и на некоторых моих тестах выполняются быстрее Loom-корутин.
Kotlin-корутины - универсальное кроссплатформенное решение, не требующее поддержки со стороны рантайма. Что важно, учитывая популярность Kotlin на Android и таргетинг на разные платформы. Loom-корутины же требуют JDK, причем одной из самых новых версий.
Я бы к минусам корутин еще отнес - чтобы их эффективно использовать все IO вызовы внутри также должны уметь поддерживать корутины(быть не блокирующими), иначе при попытке вызова обычного блокирующего java метода внутри suspend функции это ни к чему хорошему не приведет. И об этом надо помнить постоянно(в отличии от Loom), а попытки решить эту проблему "разукрашивают" код еще сильнее. Похожий тред есть на stackoverflow.
Но, справедливости ради, Kotlin-корутины не обязывают использовать только их для эффективного IO.
Решение можно комбинировать: если `Dispatchers.IO` (диспатчер, в котором принято запускать блокирующий код в Kotlin) запускать на виртуальных потоках, то можно автоматически получить неблокирующие IO вызовы.
Другое дело, что Loom-корутинам в принципе не нужно раскрашивать код и их проще отлаживать
Но, справедливости ради, Kotlin-корутины не обязывают использовать только их для эффективного IO.
Это понятно. Но проблема как была так и остается - с использованием блокирующего кода внутри корутин - неважно IO или что то другое. И хорошо если IDE умеет распознавать такие случаи и подсказывать. А так это потенциально опасное место чтобы выстрелить себе в ногу. Именно этот минус я и имел ввиду.
По второму пункту вы сами ответили на свой вопрос - какой смысл использовать корутины, если есть возможность использовать JDK21 и выше.
У Loom этот минус тоже проявляется, пусть и заметно слабее. Тем не менее, для него тоже существуют вещи, которые лучше бы в виртуальном потоке не делать (и, увы, банальный synchronized метод входит в их число)…
Про synchronized
конечно верно.
Но его и в Kotlin-корутинах нельзя использовать. Это общая проблема блокировок в асинхронном коде, существующая как и в Kotlin- , так в Loom-корутинах
Корутины Kotlin хотя бы "красные" и имеют свой собственный "красный" аналог synchronized.
А виртуальные потоки притворяются "синими", своего аналога synchronized не имеют, но при этом с системным конфликтуют.
конкретно с synchronized
, да пока Loom работает плохо, но c другими примитивами синхронизации(ReentrantLock
, Semaphore
и т.д.) виртуальные потоки работать умеют.
Для конкретно моих Loom-корутин добавить аналог kotlinx.coroutines.sync.Mutex
не сложно, но да нужно будет либо полностью переходить на свои примитивы синхронизации, либо держать в голове, можем ли мы воспользоваться в данном куске кода стандартными средствами синхронизации или нет.
Это, пожалуй, основной аргумент в пользу раскраски кода
Но можно сделать раскраску кода обязательной и для Loom-корутин, воспользовавшись аннотациями и дополнительными линтерами, которые будут проверять раскраску во время сборки проекта.
Обратите внимание, что в примере нет ни одного колбека
А вот это вот что:
button.addActionListener(e -> dispatch(...
Воообще корутины это про то, где надо высоко конкурентный код писать,
виртуальный потоки это про то, что надо кучу последовательных запросов (в другие подсистемы) сделать на блокирующие запросы, при этом сервер сам сможет кучу таких запросов выдержать
При этом в корутины котлина, чтобы интегрировать виртуальные потоки для блокироющего IO, надо заместо Dispatchers.IO, на Executors.newVirtualThreadPerTaskExecutor().AsCoroutineDispatcher() заменить
Но имхо, Dispatchers.DEFAULT будет быстрее (тк нет оверхеда на манипуляции со стеком), и надо использовать не блокирующие IO, если у вас Spring, используйте WebClient, там под капотом на тред пуле с обычными потоками максимально эффективно все сделается. А результат вам уже можно будет обработать в Dispatchers.DEFAULT
Всё так. Единственное, с чем не соглашусь - это
корутины это про то, где надо высоко конкурентный код писать
Коротины - это скорее про удобство разработки, про то чтобы писать асинхронный код в обычном легко читаемом синхронном стиле, без колбек хела.
Ну и статья в целом, о том что Project Loom не ограничивается виртуальными потоками: с помощью не публичного API можно реализовать свои корутины, отличающиеся от Котлиновских
корутины это про то, где надо высоко конкурентный код писать
Я вот корутины только в общих чертах представляю, т.к. на Java пишу. Расскажите как они помогают именно в конкурентной среде. Там какие-то удобные инструменты синхронизации и распараллеливания задач?
Если сравнивать их с классическим многопоточным кодом, то корутины сильно сокращают число потоков, а с ним и использование памяти.
Если сравнивать их с высокоэффективной асинхронной лапшой - то корутины помогают не сойти с ума при написании этой лапши, и дают такие удобные инструменты, как последовательность инструкций, условный оператор, оператор цикла, подпрограммы и прочие порождения структурного программирования.
Классический многопоточный это new Thread().start()
? Не думаю, что в высококонкурентных приложениях так ещё хоть кто-то пишет.
Второй параграф про асинхронщину, которую Loom и упрощает.
Короче, я пока не понял какие именно преимущества даёт использование корутин в конкурентной среде. Мне кажется, что никаких, но я ими и не пользовался никогда, потому и спрашиваю.
Да, разумеется, если сравнивать корутины именно с Loom, то выходит примерно одинаково. Они чуть быстрее из-за отсутствия необходимости перекладывать локальные переменные со стека в кучу и обратно, но чуть медленнее из-за количества аллокаций при глубокой вложенности.
А вот насчёт "так никто не пишет" я бы поспорил. Ведь суть Loom - именно в том, чтобы продолжать писать так, как писали раньше. Только вместо new Thread
теперь Thread.ofVirtual
Суть Loom в том, чтобы вообще ничего не писать, но получить прирост производительности за счёт неявной асинхронщины.
Создавать треды вручную - это вообще плохая практика кмк: либо это какая-то библиотека для многопоточки (маловероятно), либо стоит использовать уже готовые инструменты типа executor service (многовероятно).
Executor service работает же не на магии, а точно так же создаёт потоки. А дальше либо он используется блокирующим образом (и тогда с точки зрения производительности ничем не отличается от классического многопоточного сервера), либо его пытаются использовать асинхронно (и тогда получается асинхронная лапша).
Я вам про одно, а вы мне про другое - какой-то диалог в никуда.
Executor service работает же не на магии
Я где утверждал обратное?
Короче, не надо уводить дискуссию в сторону. Аргументов в пользу корутин для конкурентной среды я не увидел. Loom решает схожую задачу (continuations) и делает это бесшовно (читай лучше).
Project Loom. Не только виртуальные потоки