Фон:
Я спрашиваю об этом, потому что в настоящее время у меня есть приложение со многими (от сотен до тысяч) потоков. Большинство из этих потоков простаивают большую часть времени, ожидая, пока рабочие элементы будут помещены в очередь. Когда рабочий элемент доступен, он затем обрабатывается путем вызова некоторого произвольно сложного существующего кода. В некоторых конфигурациях операционной системы приложение сталкивается с параметрами ядра, определяющими максимальное количество пользовательских процессов, поэтому я бы хотел поэкспериментировать со средствами для сокращения числа рабочих потоков.
Мое предлагаемое решение:
Похоже, что подход на основе сопрограммы, где я заменяю каждый рабочий поток сопрограммой, поможет выполнить это. Тогда у меня может быть рабочая очередь, поддерживаемая пулом реальных (ядро) рабочих потоков. Когда элемент помещается в определенную очередь очереди для обработки, запись будет помещена в очередь пула потоков. Затем он возобновит соответствующую сопрограмму, обработает свои данные в очереди, а затем снова приостановит ее, освободив рабочий поток, чтобы выполнить другую работу.
Детали реализации:
В размышлениях о том, как я это сделаю, у меня возникли проблемы с пониманием функциональных различий между stackless и stackful сопрограммами. У меня есть опыт использования stackful coroutines с помощью библиотеки Boost.Coroutine. Я считаю, что это относительно легко понять с концептуального уровня: для каждой сопрограммы она поддерживает копию контекста и стека процессора, а когда вы переключаетесь на сопрограмму, она переключается на этот сохраненный контекст (как и планировщик режима ядра).
Что менее понятно для меня, так это то, как отличается от этого нестрогая сопрограмма. В моей заявке очень важно количество накладных расходов, связанных с вышеописанной очередностью рабочих элементов. Большинство реализаций, которые я видел, например, новой библиотеки CO2, предполагают, что стекированные сопрограммы обеспечивают намного более низкие контекстные переключатели контекста.
Поэтому я хотел бы более четко понять функциональные различия между stackless и stackful coroutines. В частности, я думаю об этих вопросах:
-
Ссылки, подобные этому, свидетельствуют о том, что различие заключается в том, где вы можете получить/возобновить в стеке или без стекирования. Это так? Есть ли простой пример того, что я могу сделать в стеке, но не в виде стека?
-
Существуют ли какие-либо ограничения на использование переменных автоматического хранения (т.е. переменных "в стеке" )?
-
Существуют ли какие-либо ограничения на то, какие функции я могу вызывать из стекированной сопрограммы?
-
Если нет сохранения контекста стека для стекированной сопрограммы, где переменные автоматического хранения идут при выполнении сопрограммы coroutine?