Здравствуйте, Videoman, Вы писали: V>Твой WaitingBlock сейчас везде называется Promise. Это не проблема и есть уже давно. Смотри пара std::promise/std::future. Проблема же не в отложенном ожидании как таковом, а в восстановлении всего контекста ожидания, что бы продолжить бизнес логику дальше, с того же места, где она прервалась на ожидание.
Не-а. promise/future — это примитивы. Если их прямо использовать, то понять что-либо будет несколько затруднительно. Поэтому я и предлагаю сделать обвязку в виде функциональных объектов. Комбинация таких объектов позволит организовывать код в простую и понятную последовательность.
Скрытый текст
V>Вот пример:
V>result class::some_method1(params1_t... params)
V>{
V> Что создаем на стеке (1)
V> Что создаем на стеке (2)
V> Что создаем на стеке (3)
V> wair_for_io_operation (1)
V> wair_for_io_operation (2)
V> Что создаем на стеке (4)
V> Что создаем на стеке (5)
V> Что создаем на стеке (6)
V> wair_for_io_operation (3)
V> wair_for_io_operation (4)
V> Очищаем созданное на стеке (1)
V> Очищаем созданное на стеке (2)
V> Очищаем созданное на стеке (3)
V> Очищаем созданное на стеке (4)
V> Очищаем созданное на стеке (5)
V> Очищаем созданное на стеке (6)
V>}
V>result class::some_method2(params2_t... params)
V>{
V> Что создаем на стеке (1)
V> Что создаем на стеке (2)
V> Что создаем на стеке (3)
V> wair_for_io_operation (1)
V> wair_for_io_operation (2)
V> Что создаем на стеке (4)
V> Что создаем на стеке (5)
V> Что создаем на стеке (6)
V> wair_for_io_operation (3)
V> wair_for_io_operation (4)
V> Очищаем созданное на стеке (1)
V> Очищаем созданное на стеке (2)
V> Очищаем созданное на стеке (3)
V> Очищаем созданное на стеке (4)
V> Очищаем созданное на стеке (5)
V> Очищаем созданное на стеке (6)
V>}
V>
Нужно иметь возможность выполнять эти два метода параллельно, в зависимости от того, как завершаются операции ввода-вывода. В С++20 корутины между вызовами сохраняют объекты выделенные на стеке в куче и после ожидания заново копируют на стек в том же состоянии, для продолжения выполнения с прерванной точки. Как такое можно сделать на С++17 по твоему?
Насколько я понимаю, корутины в С++20 достаточно бесполезны. Нужно сразу С++23, в котором являются объекты, позволяющие нормально организовать код. Честно говоря, я не понимаю зачем понадобилось вводить ключевые слова, вроде co_yield. Для совмещения параллельности выполнения с ленивыми вычислениями мне это кажется излишним.
Вот если мы создали какой-то ленивый объект. Он хранит одно состояние и умеет переходить в следующее при выдаче результата. Хорошо. Очевидно, что если внутри этого объекта реализовать некое вычисление следующего состояния параллельно к работе основного потока, то мы получим сопрограмму. Чем это принципиально отличается от корутин — Зачем нужны co_await с co_return'ами —
Вот если посмотреть на тот псевдокод, что вы написали, то можно заметить, что он написан исключительно в процедурном стиле, здесь создаём, тут ждем, там запускаем. А где место объекту? Если же начать думать в парадигме объектов, то всё станет намного проще. Собственно, не спроста в C++23 пришли std::generator. Это закономерный и, к моему сожалению, эволюционный путь развития.
Здравствуйте, B0FEE664, Вы писали:
BFE>Насколько я понимаю, корутины в С++20 достаточно бесполезны. Нужно сразу С++23, в котором являются объекты, позволяющие нормально организовать код. Честно говоря, я не понимаю зачем понадобилось вводить ключевые слова, вроде co_yield. Для совмещения параллельности выполнения с ленивыми вычислениями мне это кажется излишним.
Чем мне поможет С++23 c задачами ввода/вывода? Стандартизация самого механизма уже есть в С++20, а поддержки I/O не предвидится даже в С++29, насколько я понял. Всё равно всё придется делать самомому.
BFE>Вот если мы создали какой-то ленивый объект. Он хранит одно состояние и умеет переходить в следующее при выдаче результата. Хорошо. Очевидно, что если внутри этого объекта реализовать некое вычисление следующего состояния параллельно к работе основного потока, то мы получим сопрограмму. Чем это принципиально отличается от корутин — Зачем нужны co_await с co_return'ами —
Вычисление можно организовать параллельно. А как ожидать такие waiter'ы одновременно? Допустим у тебя 10 таких операций выполняется параллельно, все сильно разные по времени. Если ты раньше времени зависнешь на ожидании долгого события, то более быстрые останутся заблокированными. Как ты предлагаешь решить эту проблему?
Корутины сами решают какую из сопрограмм возобновить и в какой момент восстановить стек и продолжить.
BFE>Вот если посмотреть на тот псевдокод, что вы написали, то можно заметить, что он написан исключительно в процедурном стиле, здесь создаём, тут ждем, там запускаем. А где место объекту? Если же начать думать в парадигме объектов, то всё станет намного проще. Собственно, не спроста в C++23 пришли std::generator. Это закономерный и, к моему сожалению, эволюционный путь развития.
Генераторы не обязаны работать параллельно, там немного другая задача — передать управление и возобновить опять с прерванного места, при этом нет никаких ожиданий, поток CPU используется на все 100%. Такое я ещё могу представить как можно сделать без поддержки корутин.
в азио есть несколько политик по работе с асинхронностью, задаются так называемыми completion token, которые определяют механизм возврата результата асинхронной функции. В частности есть токен use_future, для него асинх. операция возвратит фьючер
// передаём токен use_future последним аргументом
std::string line;
std::future<std::size_t> future_read =
boost::asio::async_read_until(socket, boost::asio::dynamic_buffer(line), '\n', boost::asio::use_future);
// get() ждёт результат, подобно await в javascriptconst size_t bytes_transferred = future_read.get();
if (bytes_transferred != 0)
{
// do something with line
}
Здравствуйте, antropolog, Вы писали:
A>в азио есть несколько политик по работе с асинхронностью, задаются так называемыми completion token, которые определяют механизм возврата результата асинхронной функции. В частности есть токен use_future, для него асинх. операция возвратит фьючер
Супер, отлично! Но, стоит чуть усложнить логику и такой подход не работает:
С ассиметричными безстековыми корутинами можно ожидать func1(), func2(), func3() параллельно и будить по мере загрузки I/O.
Как такое проделать с boost::asio::use_future ?
Здравствуйте, Videoman, Вы писали:
V>С ассиметричными безстековыми корутинами можно ожидать func1(), func2(), func3() параллельно и будить по мере загрузки I/O. V>Как такое проделать с boost::asio::use_future ?
кмк никак, ну т.е. это функциональность корутин, и без корутин такое не сделать
у меня правда немного вбок вопрос, а вы что там процессите? просто (судя по нику) если это видеостримы, т.е. что-то тяжёлое, то я бы вероятно вообще не заморачивался бы с асинхронностью и сделал бы просто синхронный код и заспаунил потоков по количеству стримов
Здравствуйте, antropolog, Вы писали:
A>у меня правда немного вбок вопрос, а вы что там процессите? просто (судя по нику) если это видеостримы, т.е. что-то тяжёлое, то я бы вероятно вообще не заморачивался бы с асинхронностью и сделал бы просто синхронный код и заспаунил потоков по количеству стримов
Стримы как раз процесятся без проблем с потоками или пулами потоков. Но последнее время также появляется куча логики которая специфична для сервисов, когда приходит масса разных запросов, потом эти запросы обрабатываются и обратно возвращаются ответы, не требует CPU. Это требует кучу сетевого взаимодействия с другими сервисами. Всё это появляется из-за плотного взаимодействия с web стеком, а там асинхронность это базовая штука.