История изменений
Исправление shahid, (текущая версия) :
другие файберы вытеснят ожидающий таск с треда и займутся другими делами
Очень грубо говоря, есть thread-pool и очередь задач.
В пуле кол-во потоков равно кол-ву CPU.
Очередь задач раскидывает задачи по свободным потокам, по возможности учитывая cpu cache coherence.
В случае асинхронной работы с базами будет маленькая задача «отправить запрос к базе» и листенер в netty, который потом отправит на исполнение новую задачу: «обработать результат запроса». Т.е. «вытеснения» как такового нет, и паразитные context switch снижены до минимума. По такому принципу устроен например play framework. Но такая работа на блокирующем io заблокирует весь сервис очень быстро, и для блокирующих операций тут нужен отдельный thread-pool (ExecutionContext).
На erlang есть глобальный thread-pool легких тредов, где их обычно тысячи и миллионы. Размер thread-pool задается параметром при запуске beam (это vm erlang'а). Большой thread pool жрёт много RAM и повышает долю паразитных, хоть и легковесных, переключений контекста на CPU. Я не считаю, что модель многопоточности erlang хороша: неоправданные расходы на RAM, на серьезной нагрузке ощущаются потери на переключениях и нужно вручную педалировать per-thread GC, для обмена сообщениями между потоками используется memcpy() либо сериализация в блобы. Но из-за отсутствия изменяемых типов данных в erlang и глобального GC что-то лучше придумать там проблематично.
Исправление shahid, :
другие файберы вытеснят ожидающий таск с треда и займутся другими делами
Очень грубо говоря, есть thread-pool и очередь задач.
В пуле кол-во потоков равно кол-ву CPU.
Очередь задач раскидывает задачи по свободным потокам, по возможности учитывая cpu cache coherence.
В случае асинхронной работы с базами будет маленькая задача «отправить запрос к базе» и листенер в netty, который потом отправит на исполнение новую задачу: «обработать результат запроса». Т.е. «вытеснения» как такового нет, и паразитные context switch снижены до минимума. По такому принципу устроен например play framework. Но такая работа на блокирующем io заблокирует весь сервис очень быстро, и для блокирующих операций тут нужен отдельный thread-pool (ExecutionContext).
На erlang есть глобальный thread-pool легких тредов, где их обычно тысячи и миллионы. Размер thread-pool задается параметром при запуске beam (это vm erlang'а). Большой thread pool жрёт много RAM и повышает долю паразитных, хоть и легковесных, переключений контекста на CPU. Я не считаю, что модель многопоточности erlang хороша: неоправданные расходы на RAM, на серьезной нагрузке ощущаются потери на переключениях и нужно вручную педалировать per-thread GC, для обмена сообщениями между потоками используется memcpy() либо сериализация в блобы. Но из-за отсутствия изменяемых типов данных в erlang что-то лучше придумать там проблематично.
Исходная версия shahid, :
другие файберы вытеснят ожидающий таск с треда и займутся другими делами
Очень грубо говоря, есть thread-pool и очередь задач.
В пуле кол-во потоков равно кол-ву CPU.
Очередь задач раскидывает задачи по свободным потокам, по возможности учитывая cpu cache coherence.
В случае асинхронной работы с базами будет маленькая задача «отправить запрос к базе» и листенер в netty, который отправит на исполнение новую задачу: «обработать результат запроса». Т.е. «вытеснения» как такового нет, и паразитные context switch снижены до минимума. По такому принципу устроен например play framework. Но такая работа на блокирующем io заблокирует весь сервис очень быстро, и для блокирующих операций тут нужен отдельный ExecutionContext.
На erlang есть глобальный thread-pool легких тредов, где их обычно тысячи и миллионы. Размер thread-pool задается параметром при запуске beam (это vm erlang'а). Большой thread pool жрёт много RAM и повышает долю паразитных, хоть и легковесных, переключений контекста на CPU. Я не считаю, что модель многопоточности erlang хороша: неоправданные расходы на RAM, на серьезной нагрузке ощущаются потери на переключениях и нужно вручную педалировать per-thread GC, для обмена сообщениями между потоками используется memcpy() либо сериализация в блобы. Но из-за отсутствия изменяемых типов данных в erlang что-то лучше придумать там проблематично.