Комментарии 27
практически все рассказывающие о виртуальных потоках отмечают, что их не рекомендуется применять для кода с большой интенсивностью и количеством syncronized блоков (не знаю понятно ли это из объяснения в статье, но если коротко: блокирующая операция в синхронизированном блоке "приклеивает"(литературный перевод pin), т.е. блокирует платформенный поток) и вообще они предполагаются не для задач использующих общие ресурсы и не для замены платформенных потоков, а тут кажется они "наоборот" пытаются сделать из томкета nodejs
а тут кажется они "наоборот" пытаются сделать из томкета nodejs
Скорее как таски с async-await в dotnet-е. Там запретили в async методах использовать lock для решения этой проблемы.
таски с async-await в dotnet-е. Там запретили в async методах использовать lock для решения этой проблемы.
Если быть точным, то не запретили.
Запретили использовать await внутри lock:
async Task<int> DoAsync()
{
lock (this)
{
await Task.Delay(10); // CS1996 Cannot await in the body of a lock statement
}
return 7;
}
А lock внутри async - пожалуйста:
async Task<int> DoAsync()
{
await Task.Delay(10);
lock (this)
{
// do something
}
await Task.Delay(10);
return 7;
}
Инженеры Netflix отправили несколько независимых отчетов о периодических таймаутах и случаях подвисания в команды
А до этого инженеры Нетфликс думали что красивое название
Виртуальные потоки
По щучьему велению решит всё проблемы асинхронного кода.
Вы будете смеяться, но как только разберутся с synchronized (а это с очень высокой вероятностью произойдет в ближайших релизах https://news.ycombinator.com/item?id=39012631), то решит если не "всё" проблемы многопоточности, то по крайней мере избавит нас от необходимости писать жуткий асинхронный код на колбэках, при этом не мучаясь с async await, которые в других языках делят код на два мира.
я боюсь что, действительно, можно будет только посмеяться.
Мне,например, довелось и 20+ лет назад работать с кодом который очевидно избежал
необходимости писать жуткий асинхронный код на колбэках
при использовании многопоточности, когда async await -ов еще в помине не было.
Попробую чуть более развернуто ответить, если вы не до конца меня поняли.
Вполне вероятно, что ваш код 20+ лет был обычным кодом на потоках ОС. Многопоточный код на потоках ОС - это ОК. Но если таких потоков создать много, то программа дальше функционировать не сможет, поскольку ресурсы закончатся. А много их создать хочется, потому что значительную часть времени потоки находятся в процессе выполнения I/O операций, а процессор простаивает. Т.е. естественным желанием является увеличить количество потоков обработки, чтобы загрузить процессор, но в классической модели так сделать не получится.
Именно эту проблему решает "жуткий асинхронный код на колбэках", который позволяет оптимально загрузить железо ценой удобства разработки. И именно эта цена, которую пришлось заплатить, не устраивала значительное количество людей по всему миру, поэтому и появились async/awaitы, горутины и т.д, которые позволяли писать "как-бы" синхронный код, в котором I/O операции не требовали держать поток ОС.
В Java аналога такой функциональности "как-бы" синхронного кода с оптимальной загрузкой потоков ОС долго не было, пока не появился Loom. Его основное преимущество по сравнению с другими языками, например C# async/await в том, что никаких дополнительных ключевых слов использовать не нужно и JVM всё делает за вас, просто беря ваш код и понимая, в какие моменты его выполнение нужно "отцепить" от потока ОС (например, на операции ввода/вывода) и когда "прицепить" обратно.
Концептуально великолепно, но то, что ушло в 21 версии в релиз к сожалению не умеет правильно обрабатывать synchronized блоки/методы и поскольку значительное количество (в первую очередь старых) библиотек их используют, то это приводит к таким вот ситуациям, которая описана в статье. Проблема с synchronized непростая, но не нерешаемая и ее скоро решат. И вот тогда, действительно можно будет говорить о том, что "всё" проблемы многопоточности (применительно к конкретной ситуации оптимального использования железа для выполнения обычного синхронного кода) действительно будут решены.
Вполне вероятно, что ваш код 20+ лет был обычным кодом на потоках ОС.
Без всякого "вероятно" я совершенно определенно могу сказать что этот 20+ лет код (только не был, а есть, и возможно даже что даже вы периодически пользуетесь продуктами на его основе) сделан на интерфейсах. Дело в том что те интерфейсы спроектированы так что вы не знаете есть ли поток под данным конкретным интерфейсом или он работает совершенно синхронно, просто надо уметь проэктировать правильное взаимодействие и вообще архитектуру на интерфейсах, раньше это умели делать, а теперь ищем способ кто бы сделал это за нас.
Казалось бы причем здесь интерфейсы?... То о чем мы говорим - это не вопрос использования/не использования абстракций, а вполне конкретный вопрос о том, как сделать так, чтобы программа выглядела синхронной (с использованием интерфейсов или без них), но при этом не занимала потоки ОС в моменты ожидания. Раньше такого действительно не было, может только если какой-нибудь Erlang так умел, но в Java да и в других языках 20+ лет назад о таком даже не думали, потому что проблемы и приоритеты были другие.
но в Java да и в других языках 20+ лет назад о таком даже не думали
Это вы лихо хватили.
https://en.m.wikipedia.org/wiki/Green_thread
In Java 1.1, green threads were the only threading model used by the Java virtual machine (JVM),[8] at least on Solaris.
Спасибо переводчику за хорошую статью, так бы я ее конечно не нашел... но читать лучше в оригинале, простите.
Помню, когда виртуальные потоки находились ещё в разработке, весь интернет, включая Хабр, пестрел статьями о том, как они сразу же и волшебным образом поднимут производительность многопоточных программ на Java, не требуя никаких изменений в коде. Тогда комментарии с моими сомнениями на этот счёт обильно минусовали. Сильна у людей вера в чудеса и страсть к халяве.
Когда решится проблема с synchronized, а она решится в ближайшем будущем, то это действительно будет так.
вам плюс за оптимизм, в любом случае.
Это не оптимизм, а реализм. Во-первых Loom уже сейчас работает вполне неплохо и если у вас везде используются только lockи, без synchronized вы уже прямо сейчас можете наблюдать то самое "повышение производительности" (а если точнее, более оптимальное использование железа при большом количестве I/O операций). Во-вторых ничего сверхсложного в том, чтобы synchronized тоже "починился" нет, поскольку в конечном счете его можно на уровне Jvm "свести" к операциям с локами, которые даже текущий Loom поддерживает.
Да и в целом если вы задумаетесь про Loom на концептуальном уровне - есть ли у JVM в рантайме вся информация для того, чтобы понять, что ваш поток сейчас чего-то ждёт и процессор не занимает? Конечно есть! В реализации могут быть нюансы, первый блин комом, но концептуально здесь всё довольно просто. Вспомните parallel Streamы, которые на релизе в J8 работали значительно медленнее, чем в J9.
Так что я стараюсь реалистично смотреть на эту ситуацию.
Без сарказма. Можете поделиться источниками, как и когда проблема будет решана?
Сейчас идёт активная дискуссия, о том, как решить эту проблему технически, например вот один из тредов на эту тему: https://mail.openjdk.org/pipermail/loom-dev/2024-February/006433.html . Лично у меня нет сомнений, что эту проблему решат, поскольку с одной стороны, проблема действительно большая, а с другой Loom больше не preview фича и что-то сделать им точно придётся. Я предполагаю что следующий LTS уже будет выпущен с решением этой проблемы.
Проблема с synchonized блоками уже решена в тестовых билдах, думаю войдет в слебдующую джаву(24).
https://mail.openjdk.org/pipermail/loom-dev/2024-May/006632.html
Draft JEP: https://openjdk.org/jeps/8337395
Она уже решена, вы можете скачать jdk23-ea и собрать самостоятельно, все интеграционные тесты она проходит. Релиз выйдет через месяц.
Скрытый текст
https://mail.openjdk.org/pipermail/loom-dev/2024-February/006433.html
Это решится на уровне JDK?
Ежу понятно что породить стопятьсот потоков который понаоткрывают сокеты и всё сломаеться, нафига баян с потоками, если этим должна заниматься ОС, а не приложуха, Цезарево - Цезарю, как говориться.
А чему ломаться, если ресурсы позволяют? https://github.com/smallnest/C1000K-Servers вот пожалуйста люди еще 9 лет назад экспериментировали и разгоняли на одной машинке 1.2 миллиона вебсокет конекшенов в том числе на Jvm и всё в порядке. Вот только на код там без слез нельзя смотреть и без мануала разобраться, это да. Идея Loom в том, что можно обойтись и без слёз при этом не сильно меняя свой код.
Это ж обычные фиберы, в Винде они наверное ещё со времён NT, как и в линуксах с их swapcontext. Разрабы джавы не смогли в нормальный планировщик фиберов на пуле потоков? Вообще, переход на асинхронность должен менять мозг разработчиков в части устройства платформы. Для качественной работы там надо убирать все блокировки, заменяя на нотификации, а "спать" реальный поток должен только в одном случае - когда нет заданий в очереди. Не получилось задаче захватить ресурс - подпишись на его освобождение и отдай поток другой задаче.
нагуглил статейку в которой рассказывается как обнаруживать запинивающиеся потоки:
https://todd.ginsberg.com/post/java/virtual-thread-pinning/
Виртуальные потоки Java 21 — чувак, где мой lock?