Re[6]: Вопрос по корутинам
От: so5team https://stiffstream.com
Дата: 26.08.25 07:04
Оценка: :)
Здравствуйте, Евгений Музыченко, Вы писали:

_>>Вместо явной передачи управление её стараются скрыть под капотом, более того внетреннее состояние тоже прячут.


ЕМ>Это все от того, что развитием C++ в основном уже давно занимаются люди, понимающие программирование, как "запись алгоритма средствами языка", а не как обеспечение выполнения нужных действий с нужной степенью понятности, эффективности, применимости и т.п.


Скорее всего вы просто сильно в стороне от многих задач, которые сейчас решаются на C++.
Re[3]: Вопрос по корутинам
От: rg45 СССР  
Дата: 26.08.25 07:37
Оценка: 1 (1) +2
Здравствуйте, kov_serg, Вы писали:

_>Мне больше по душе постепенно исполняемы функции, чем сопрограммы(корутины). Они явно требую кванта исполнения, в отличии от "сопрограм" которые вызываются (где попало) в разных тредах, колбяках, без каких либо ограничений.


Так корутины (сопрограммы) именно такими и являются — последовательно исполняемыми. Никаких разных тредов и вызовов где попало здесь нет. Пока одна сопрограмма выполняется, другая ожидает (suspended). Отличие лишь в количестве "квантов".

--
Справедливость выше закона. А человечность выше справедливости.
Re[5]: В написании линейного кода, который прерывается сетью
От: rg45 СССР  
Дата: 26.08.25 07:40
Оценка: +1
Здравствуйте, so5team, Вы писали:

S>Вроде бы несколько лет назад на Хабре была статья от Антона Полухина, где он показывал, как stackless короутины можно перебрасывать с одного треда на другой: https://habr.com/ru/companies/yandex/articles/420861/


Конечно, многопоточность и корутины можно использовать и совместно, в разнообразных вариантах. В то же время многопоточность не является встроенным свойством корутин.
--
Справедливость выше закона. А человечность выше справедливости.
Отредактировано 26.08.2025 7:53 rg45 . Предыдущая версия .
Re: Вопрос по корутинам
От: Doom100500 Израиль  
Дата: 26.08.25 08:32
Оценка:
Здравствуйте, LaptevVV, Вы писали:

LVV>А в каких задачах корутины вот прям супер — супер?

LVV>Чего раньше приходилось делать муторно и долго ?

Например, в однопоточном UI. Но непонятно зачем это делать на плюсах, да ещё и самому руками, когда есть готовые и отлаженные C++ UI библиотеки.
Спасибо за внимание
Re[4]: Вопрос по корутинам
От: kov_serg Россия  
Дата: 26.08.25 08:42
Оценка:
Здравствуйте, rg45, Вы писали:

R>Так корутины (сопрограммы) именно такими и являются — последовательно исполняемыми. Никаких разных тредов и вызовов где попало здесь нет. Пока одна сопрограмма выполняется, другая ожидает (suspended). Отличие лишь в количестве "квантов".


Дело не в названии а в принипе работы. Кто определяет когда функция продолжит исполнение, кто следит за ресурсами, которые функции использует, какие гарантии, какие инварианты необходимо соблюдать для таких открытых функций?
Где это описано для корутин? Обычно нигде.
Re[5]: Вопрос по корутинам
От: rg45 СССР  
Дата: 26.08.25 08:54
Оценка: +1
Здравствуйте, kov_serg, Вы писали:

R>>Так корутины (сопрограммы) именно такими и являются — последовательно исполняемыми. Никаких разных тредов и вызовов где попало здесь нет. Пока одна сопрограмма выполняется, другая ожидает (suspended). Отличие лишь в количестве "квантов".


_>Дело не в названии а в принипе работы.


Так я и говорю о принципе работы, а не о названии.

_>Кто определяет когда функция продолжит исполнение


Так же, как и в обычной функции — вызвающая функция продолжит выполнение после того как вызываемая вернёт управление. Просто с корутинами этот принцип может работать и в обратном направлении. А может не работать — зависит от вызывающей функции.

_>, кто следит за ресурсами, которые функции использует, какие гарантии,


Всё то же самое, что и с обычными функциями. И гарантии ни чем не хуже.

_> какие инварианты необходимо соблюдать для таких открытых функций?

_>Где это описано для корутин? Обычно нигде.

Да полно материалов, где описано, как устроены и как работают корутины. И книги есть, и ролики на ютубе. Начать можно отсюда: https://en.cppreference.com/w/cpp/language/coroutines.html
--
Справедливость выше закона. А человечность выше справедливости.
Re[5]: Вопрос по корутинам
От: rg45 СССР  
Дата: 26.08.25 09:26
Оценка:
Здравствуйте, kov_serg, Вы писали:

R>>Так корутины (сопрограммы) именно такими и являются — последовательно исполняемыми. Никаких разных тредов и вызовов где попало здесь нет. Пока одна сопрограмма выполняется, другая ожидает (suspended). Отличие лишь в количестве "квантов".


_>Дело не в названии а в принипе работы. Кто определяет когда функция продолжит исполнение, кто следит за ресурсами, которые функции использует, какие гарантии, какие инварианты необходимо соблюдать для таких открытых функций?

_>Где это описано для корутин? Обычно нигде.

И я что-то не очень понимаю, о каких гарантиях ты говоришь.

Например, каких гарантий тебе не хватает в примере ниже?

http://coliru.stacked-crooked.com/a/8c7737e8d549bffb

#include <concepts>
#include <generator>
#include <iostream>

template <std::incrementable T> 
std::generator<const T&> lazy_sequence(T t, size_t count) {
    for (size_t i = 0; i < count; ++i)
        co_yield t++;
}
 
int main()
{
    for (auto t : lazy_sequence(1, 10))  // -> 1 2 3 4 5 6 7 8 9 10 
        std::cout << t << ' '; 

    std::cout << std::endl;

    for (auto t : lazy_sequence('A', 26))  // -> A B C D E F G H I J K L M N O P Q R S T U V W X Y Z 
        std::cout << t << ' '; 
}
--
Справедливость выше закона. А человечность выше справедливости.
Отредактировано 26.08.2025 9:34 rg45 . Предыдущая версия . Еще …
Отредактировано 26.08.2025 9:28 rg45 . Предыдущая версия .
Re: Вопрос по корутинам
От: Hоmunculus  
Дата: 26.08.25 09:27
Оценка: 28 (3)
Здравствуйте, LaptevVV, Вы писали:

Вот хороший ответ зачем корутины нужны:

https://www.youtube.com/watch?v=DLLt4anKXKU
Re[6]: Вопрос по корутинам
От: kov_serg Россия  
Дата: 26.08.25 09:51
Оценка:
Здравствуйте, rg45, Вы писали:

R>И я что-то не очень понимаю, о каких гарантиях ты говоришь.

R>Например, каких гарантий тебе не хватает в примере ниже?

R>http://coliru.stacked-crooked.com/a/8c7737e8d549bffb


R>
R>#include <concepts>
R>#include <generator>
R>#include <iostream>

R>template <std::incrementable T> 
R>std::generator<const T&> lazy_sequence(T t, size_t count) {
R>    for (size_t i = 0; i < count; ++i)
R>        co_yield t++;
R>}
 
R>int main()
R>{
R>    for (auto t : lazy_sequence(1, 10))  // -> 1 2 3 4 5 6 7 8 9 10 
R>        std::cout << t << ' '; 

R>    std::cout << std::endl;

R>    for (auto t : lazy_sequence('A', 26))  // -> A B C D E F G H I J K L M N O P Q R S T U V W X Y Z 
R>        std::cout << t << ' '; 
R>}
R>


Это слишком простой пример. Тут никаких особых гарантий не надо, т.к. они не используют общих ресурсов и даже не выделяют их для выполнения зачачи.
Простой вопрос кто должен выделять и освободлать ресурсы исполнитель или прораб тот кто поставил задачу должен предоставить ресурсы для её выполнения?
Re[7]: Вопрос по корутинам
От: rg45 СССР  
Дата: 26.08.25 10:07
Оценка: +1
Здравствуйте, kov_serg, Вы писали:

_>Это слишком простой пример. Тут никаких особых гарантий не надо, т.к. они не используют общих ресурсов и даже не выделяют их для выполнения зачачи.

_>Простой вопрос кто должен выделять и освободлать ресурсы исполнитель или прораб тот кто поставил задачу должен предоставить ресурсы для её выполнения?

Что-то не понимаю я тебя. Правила всё те же, что и для обычных функций. Ресурсы могут быть выделены вызывающей корутиной, а освобождаться в вызываемой, а может быть и наоборот. Но позаботиться об освобождении ресурсов, в добротной программе, должен тот, кто их выделяет. При необходимости можно использовать RAII. Только при чём здесь корутины вообще? Это общие правила, которые действуют в равной степени как для корутин, так и для обычных функций. До тех пор, пока язык гарантирует детерминированность времён жизни объектов, прикрутить RAII поверх этого не составляет никаких проблем.

Ну или набросай пример, который заставляет тебя сомневаться. А то так можно долго обсуждать, не понимая друг друга.
--
Справедливость выше закона. А человечность выше справедливости.
Отредактировано 26.08.2025 10:15 rg45 . Предыдущая версия . Еще …
Отредактировано 26.08.2025 10:12 rg45 . Предыдущая версия .
Re[7]: Вопрос по корутинам
От: rg45 СССР  
Дата: 26.08.25 10:35
Оценка:
Здравствуйте, kov_serg, Вы писали:

_>Это слишком простой пример. Тут никаких особых гарантий не надо, т.к. они не используют общих ресурсов и даже не выделяют их для выполнения зачачи.

_>Простой вопрос кто должен выделять и освободлать ресурсы исполнитель или прораб тот кто поставил задачу должен предоставить ресурсы для её выполнения?

Ты сомневаешься, что вызываемая корутина корректно завершает время жизни своего внутреннего состояния? На счёт этого можешь быть спокоен (см. пример ниже). А с точки зрения вызывающей функции процесс практически тот же самый, что и при многократном вызове обычной функции. Просто для передачи управления используеся новое словечко co_await.

http://coliru.stacked-crooked.com/a/9e5ecbe772104372

#include <concepts>
#include <generator>
#include <iostream>

class life_time_indicator
{
public:
    life_time_indicator(const std::string id) : id(id) { std::cout << id << " constructed" << std::endl; }
    ~life_time_indicator() { std::cout << id << " destructed" << std::endl; }

    life_time_indicator(const life_time_indicator&) = delete;
    life_time_indicator& operator = (const life_time_indicator&) = delete;

private:
    std::string id;    
};

template <std::incrementable T> 
std::generator<const T&> infinite_sequence(T t, const std::string& id) {
    life_time_indicator indicator{id};
    
    for (;;)
        co_yield t++;
}
 
int main()
{
    for (auto t : infinite_sequence(1, "Numbers")) {
        std::cout << t << ' '; 
        if (t == 10)
            break;
    }
    std::cout << std::endl;

    for (auto t : infinite_sequence('A', "Letters")) {
        std::cout << t << ' '; 
        if (t == 'Z')
            break;
    }
    std::cout << std::endl;
}

Numbers constructed
1 2 3 4 5 6 7 8 9 10 Numbers destructed

Letters constructed
A B C D E F G H I J K L M N O P Q R S T U V W X Y Z Letters destructed
--
Справедливость выше закона. А человечность выше справедливости.
Отредактировано 26.08.2025 11:16 rg45 . Предыдущая версия . Еще …
Отредактировано 26.08.2025 11:12 rg45 . Предыдущая версия .
Отредактировано 26.08.2025 11:11 rg45 . Предыдущая версия .
Re[2]: Вопрос по корутинам
От: LaptevVV Россия  
Дата: 26.08.25 11:49
Оценка:
LVV>>А в каких задачах корутины
ЕМ>Вы вроде не мальчик, должны были застать слово "сопрограмма", а туда же...
Слово-то я знаю с 70-х годов.
Задач не было реальных
Хочешь быть счастливым — будь им!
Без булдырабыз!!!
Re[8]: Вопрос по корутинам
От: kov_serg Россия  
Дата: 26.08.25 12:59
Оценка:
Здравствуйте, rg45, Вы писали:

R>Ты сомневаешься, что вызываемая корутина корректно завершает время жизни своего внутреннего состояния? На счёт этого можешь быть спокоен (см. пример ниже). А с точки зрения вызывающей функции процесс практически тот же самый, что и при многократном вызове обычной функции. Просто для передачи управления используеся новое словечко co_await.


Дело как раз не в этом. То что вы показали это просто генераторы, итераторы. У них точка итерирования явная. Коротины же используются для описания асинхронных операций. Более того способ передачи им управления обычно скрыт.
Они могут вызываться внешним циклом планировщика, callback-ами (причем не факт что из основного потока).
Re[9]: Вопрос по корутинам
От: rg45 СССР  
Дата: 26.08.25 13:35
Оценка: +1
Здравствуйте, kov_serg, Вы писали:

_>Дело как раз не в этом. То что вы показали это просто генераторы, итераторы. У них точка итерирования явная. Коротины же используются для описания асинхронных операций. Более того способ передачи им управления обычно скрыт.

_>Они могут вызываться внешним циклом планировщика, callback-ами (причем не факт что из основного потока).

Нет, то, что я показал — это как раз пример использования корутин. Использование ключевого слова co_yield не оставляет возможности думать по-другому. А использованный в примерах std::generator — это просто стандартная обёртка, облегчающая работу с корутинами:

A std::generator generates a sequence of elements by repeatedly resuming the coroutine from which it was returned.


Само по себе использование корутин вовсе не означает обязательного использования асинхронных операций. Не нужно путать тёплое с мягким.

P.S. Возможно, в следующим примере использование корутин будет лучше заметно

http://coliru.stacked-crooked.com/a/f2b1f65cf422bead

#include <generator>
#include <iostream>

std::generator<char> coroutine() {
    co_yield 'C';
    co_yield 'O';
    co_yield 'R';
    co_yield 'O';
    co_yield 'U';
    co_yield 'T';
    co_yield 'I';
    co_yield 'N';
    co_yield 'E';
}
 
int main()
{
    std::cout << std::string(std::from_range, coroutine()) << std::endl; // -> COROUTINE
}

COROUTINE
--
Справедливость выше закона. А человечность выше справедливости.
Отредактировано 26.08.2025 15:04 rg45 . Предыдущая версия . Еще …
Отредактировано 26.08.2025 15:03 rg45 . Предыдущая версия .
Отредактировано 26.08.2025 15:00 rg45 . Предыдущая версия .
Отредактировано 26.08.2025 13:52 rg45 . Предыдущая версия .
Отредактировано 26.08.2025 13:50 rg45 . Предыдущая версия .
Отредактировано 26.08.2025 13:37 rg45 . Предыдущая версия .
Re[10]: Вопрос по корутинам
От: kov_serg Россия  
Дата: 26.08.25 16:59
Оценка:
Здравствуйте, rg45, Вы писали:

R>Нет, то, что я показал — это как раз пример использования корутин. Использование ключевого слова co_yield не оставляет возможности думать по-другому.

Хорошо. Как с вашей точки зрения должны смотреться минимальные требования к функции которая исполняется постепенно?
Re[11]: Вопрос по корутинам
От: rg45 СССР  
Дата: 26.08.25 18:05
Оценка: +1
Здравствуйте, kov_serg, Вы писали:

_>Хорошо. Как с вашей точки зрения должны смотреться минимальные требования к функции которая исполняется постепенно?


Ну вот здесь же описаны все требования: https://en.cppreference.com/w/cpp/language/coroutines.html

Там к самой функции требования простые: она должна использовать какие-нибудь слова из следующих: co_yield, co_return, co_await. Плюс некоторые несложные ограничения.

Гораздо более сложные требования к типу возвращаемого значения. И чтоб успростить использование, в С++23 ввели std::generator, как стандартный возвращаемы тип корутины.
--
Справедливость выше закона. А человечность выше справедливости.
Отредактировано 26.08.2025 18:19 rg45 . Предыдущая версия .
Re: Вопрос по корутинам
От: landerhigh Пират  
Дата: 26.08.25 23:18
Оценка:
Здравствуйте, LaptevVV, Вы писали:

LVV>А в каких задачах корутины вот прям супер — супер?


Конечные автоматы.

LVV>Чего раньше приходилось делать муторно и долго ?


Вот их, родимых, и приходилось делать. Как вспомню, так вздрогну.

Корутина — это же "просто" функция, которая позволяет сделать специальный return (co_yield в нынешнем стандарте), и при последующем вызове продолжить выполнение со следующей инструкции.

И вот это вот "просто" позволяет асинхронную логику лаконично реализовать в одной функции вместо развешивания лапши колбеков.
www.blinnov.com
Re[2]: Вопрос по корутинам
От: so5team https://stiffstream.com
Дата: 27.08.25 03:27
Оценка: +2
Здравствуйте, landerhigh, Вы писали:

LVV>>А в каких задачах корутины вот прям супер — супер?


L>Конечные автоматы.


А можно пример? Типа вот с КА было вот так, а с короутинами -- вот так.
Re[3]: Вопрос по корутинам
От: sergii.p  
Дата: 27.08.25 10:56
Оценка:
Здравствуйте, so5team, Вы писали:

S>А можно пример? Типа вот с КА было вот так, а с короутинами -- вот так.


вот гопота нагенерила.
Было:

#include <iostream>
#include <string>

enum class State { Idle, Started, Processing, Stopped };
 
class StateMachine {
    State state = State::Idle;

public:
    void next() {
        switch (state) {
            case State::Idle:
                std::cout << "State: Idle\n";
                state = State::Started;
                break;
            case State::Started:
                std::cout << "State: Started\n";
                state = State::Processing;
                break;
            case State::Processing:
                std::cout << "State: Processing\n";
                state = State::Stopped;
                break;
            case State::Stopped:
                std::cout << "State: Stopped\n";
                break;
        }
    }

    bool isDone() const {
        return state == State::Stopped;
    }
};

int main() {
    StateMachine sm;
    while (!sm.isDone()) {
        sm.next();
    }
    sm.next(); // финальное состояние
}


Стало:

#include <iostream>
#include <coroutine>
#include <string>

struct StateMachine {
    struct promise_type;
    using handle_type = std::coroutine_handle<promise_type>;

    struct promise_type {
        std::string value;
        auto get_return_object() { return StateMachine{handle_type::from_promise(*this)}; }
        auto initial_suspend() { return std::suspend_always{}; }
        auto final_suspend() noexcept { return std::suspend_always{}; }
        void return_void() {}
        void unhandled_exception() { std::exit(1); }
        auto yield_value(std::string s) {
            value = s;
            return std::suspend_always{};
        }
    };

    handle_type coro;

    explicit StateMachine(handle_type h) : coro(h) {}
    ~StateMachine() { if (coro) coro.destroy(); }

    bool next() {
        if (!coro.done()) {
            coro.resume();
            return true;
        }
        return false;
    }

    std::string value() const {
        return coro.promise().value;
    }
};

StateMachine runMachine() {
    co_yield "Idle";
    co_yield "Started";
    co_yield "Processing";
    co_yield "Stopped";
}

int main() {
    auto sm = runMachine();
    while (sm.next()) {
        std::cout << "State: " << sm.value() << '\n';
    }
}


Пока второй вариант многословней. Но и надо понимать, что StateMachine однотипный для всех корутин. Заново его писать не придётся. Основная бизнес-логика сосредоточена в runMachine. Тогда как в первом примере логика больше перенесена в StateMachine.
Re[4]: Вопрос по корутинам
От: rg45 СССР  
Дата: 27.08.25 11:22
Оценка: +1
Здравствуйте, sergii.p, Вы писали:

SP>Пока второй вариант многословней. Но и надо понимать, что StateMachine однотипный для всех корутин. Заново его писать не придётся. Основная бизнес-логика сосредоточена в runMachine. Тогда как в первом примере логика больше перенесена в StateMachine.


Я бы ещё добавил эквивалентный вариант с использованием std::generator для полноты картины:

http://coliru.stacked-crooked.com/a/a01d31f13490f65c

#include <generator>
#include <iostream>
#include <string>

std::generator<std::string> runMachine() {
    co_yield "Idle";
    co_yield "Started";
    co_yield "Processing";
    co_yield "Stopped";
}

int main() {
    for (const std::string& state : runMachine())
        std::cout << "State: " << state << std::endl;
}

State: Idle
State: Started
State: Processing
State: Stopped

Офигенная машина состояний. Паровоз Черепановых нервно пыхтит в коридоре
--
Справедливость выше закона. А человечность выше справедливости.
Отредактировано 27.08.2025 11:27 rg45 . Предыдущая версия .
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.