Карта сайта Kansoftware
НОВОСТИУСЛУГИРЕШЕНИЯКОНТАКТЫ
Разработка программного обеспечения

Asymmetric coroutine

Boost , Chapter 1. Coroutine2 , Coroutine

Boost C++ Libraries

...one of the most highly regarded and expertly designed C++ library projects in the world. Herb Sutter and Andrei Alexandrescu, C++ Coding Standards

PrevUpHomeNext

Два асимметричных типа корутина -корутина<>::push_typeиcoroutine<>::pull_type— обеспечивает однонаправленную передачу данных.

[Note] Note

asymmetric_coroutine<>является типизированным значениемcoroutine.

coroutine<>::pull_type

Coroutine<>::pull_typeпередает данные из другого контекста исполнения (== извлекается из). Параметр шаблона определяет тип передаваемого параметра. Конструкторcoroutine<>::pull_typeпринимает функциюcoroutine-function, принимая в качестве аргумента ссылку наcoroutine<>::push_type. Обоснованиеcoroutine<>::pull_typeпередает контроль исполненияcoroutine-функциии комплементарныйcoroutine<>::push_typeсинтезируется библиотекой и передается в качестве ссылки наcoroutine-функции.

Этот вид корутина обеспечиваеткорутин<>::pull_type::operator(). Этот метод переключает только контекст; он не передает никаких данных.

coroutine<>::pull_typeпредоставляет итераторы вводаcoroutine<>::pull_type:::iteratorиstd::begin()/std::end()Перегружены. Прирост-операция переключает контекст и передает данные.

typedef boost::coroutines2::coroutine<int>   coro_t;
coro_t::pull_type source(
    [&](coro_t::push_type& sink){
        int first=1,second=1;
        sink(first);
        sink(second);
        for(int i=0;i<8;++i){
            int third=first+second;
            first=second;
            second=third;
            sink(third);
        }
    });
for(auto i:source)
    std::cout << i <<  " ";
output:
1 1 2 3 5 8 13 21 34 55

В этом примереcoroutine<>::pull_typeсоздается в контексте основного исполнения с использованием функции лямбда (==coroutine-function), которая вычисляет числа Фибоначчи в простомдля-петли.coroutine-функциявыполняется в вновь созданном контексте исполнения, который управляется экземпляромcoroutine<>::pull_type.coroutine<>::push_typeавтоматически генерируется библиотекой и передается как ссылка на функцию лямбда. Каждый раз, когда функция лямбда вызываетcoroutine<>::push_type::operator()с другим номером Фибоначчи,coroutine<>::push_typeпереносит его обратно в основной контекст исполнения. Местное состояниекорутинной функциисохраняется и будет восстановлено при передаче управления исполнением обратнокорутинной функциидля вычисления следующего числа Фибоначчи. Потому чтоcoroutine<>::pull_typeпредоставляет итераторы ввода иstd::begin()/std::end().перегружены,диапазон на основе-петля может использоваться для итерации по генерируемым числам Фибоначчи.

coroutine<>::push_type

Coroutine<>::push_typeпередает данные в другой контекст исполнения (== push-to). Параметр шаблона определяет тип передаваемого параметра. Конструкторcoroutine<>::push_typeпринимает функциюcoroutine-function), принимая в качестве аргумента ссылку наcoroutine<>::pull_type. В отличие отcoroutine<>::pull_type, инстанцированиеcoroutine<>::push_typeне передает контроль исполненияcoroutine-функции— вместо первого вызоваcoroutine<>::push_type::operator()синтезирует комплементарныйкорутин<>::pull_typeи передает его в качестве ссылки накорутин-функцию.

Интерфейсcoroutine<>::push_typeне содержитget()-функции: вы не можете извлекать значения из другого контекста выполнения с этим видом coroutine.

coroutine<>::push_typeобеспечивает выходные итераторыcoroutine<>::push_type:::iteratorиstd::begin()/std::end()перегружены. Прирост-операция переключает контекст и передает данные.

typedef boost::coroutines2::coroutine<std::string>   coro_t;
struct FinalEOL{
    ~FinalEOL(){
        std::cout << std::endl;
    }
};
const int num=5, width=15;
coro_t::push_type writer(
    [&](coro_t::pull_type& in){
        // finish the last line when we leave by whatever means
        FinalEOL eol;
        // pull values from upstream, lay them out 'num' to a line
        for (;;){
            for(int i=0;i<num;++i){
                // when we exhaust the input, stop
                if(!in) return;
                std::cout << std::setw(width) << in.get();
                // now that we've handled this item, advance to next
                in();
            }
            // after 'num' items, line break
            std::cout << std::endl;
        }
    });
std::vector<std::string> words{
    "peas", "porridge", "hot", "peas",
    "porridge", "cold", "peas", "porridge",
    "in", "the", "pot", "nine",
    "days", "old" };
std::copy(begin(words),end(words),begin(writer));
output:
           peas       porridge            hot           peas       porridge
           cold           peas       porridge             in            the
            pot           nine           days            old

В этом примереcoroutine<>::push_typeсоздается в основном контексте исполнения, принимающем функцию лямбда (==coroutine-function), которая запрашивает строки и выкладывает их «номер» на каждой строке. Это свидетельствует об инверсии контроля, допускаемого корутинами. Без корутина функция полезности для выполнения одной и той же работы обязательно принимает каждое новое значение в качестве параметра функции, возвращаясь после обработки этого единственного значения. Эта функция будет зависеть от статической переменной состояния. Акорутинная функция, однако, может запрашивать каждое новое значение, как бы вызывая функцию, даже если ее вызывающий также передает значения, как если бы вызывая функцию. Корутинная функциявыполняется в вновь созданном контексте исполнения, который управляется экземпляром.coroutine<>::push_type. Основной контекст исполнения передает строки.корутинная функцияпо телефонуcoroutine<>::push_type::operator(). Ancooutine<>::pull_typeэкземпляр автоматически генерируется библиотекой и передается в качестве ссылки на функцию лямбда.coroutine-functionполучает доступ к строкам, переданным из основного контекста выполнения, позвонивcoroutine<>::pull_type::get()и выкладывает эти строки наstd::coutпо параметрам «номер» и «ширина». Местное состояниекорутинной функциисохраняется и будет восстановлено после передачи управления исполнением обратно.корутинная функция. Потому чтоcoroutine<>::push_typeобеспечивает итераторы вывода иstd::begin()/std::end()перегружены, алгоритмstd::copyможет использоваться для итерации по вектору, содержащему строки, и передачи их по одному в корутин.

coroutine-function

корутин-функциявозвращаетпустотуи принимает в качестве аргумента свой аналог-корутин, так что использование корутина, переданного в качестве аргументакорутин-функция, является единственным способом передачи данных и управления исполнением обратно вызывающему абоненту. Оба корутинных типа используют один и тот же шаблонный аргумент. Дляcoroutine<>::pull_typecoroutine-функциявведена вcoroutine<>::pull_typeконструкции. Дляcoroutine<>::push_typecoroutine-функцияне введена вcoroutine<>::push_typeстроительство, но введено первым вызовомкорутина<>::push_type::operator(). После возврата управления выполнением изкорутин-функциисостояние корутина может быть проверено черезкорутин<>::pull_type::operator bool, возвращающийся<true>, если корутин все еще действителенкорутин-функцияне прекращена. Если первый параметр шаблона не является<void>,<true>также подразумевает, что значение данных доступно.

passing data from a pull-coroutine to main-context

Для передачи данных изcoroutine<>::pull_typeв основной контекст фреймворк синтезируетcoroutine<>::push_type, связанный сcoroutine<>::pull_typeэкземпляром в главном контексте. Синтезированныйкорутин<>::push_typeпередается в качестве аргументакорутин-функция.coroutine-функциядолжна называть этоcoroutine<>::push_type::operator(), чтобы передать каждое значение данных обратно в основной контекст. В основном контекстеcoroutine<>::pull_type::operator boolопределяет, является ли coroutine все еще действительным и доступно значение данных илиcoroutine-functionпрекратила свою работуcoroutine<>::pull_typeявляется недействительной; значения данных не имеется. Доступ к передаваемому значению данных предоставляетсяcoroutine<>::pull_type::get().

typedef boost::coroutines2::coroutine<int>   coro_t;
coro_t::pull_type source( // constructor enters coroutine-function
    [&](coro_t::push_type& sink){
        sink(1); // push {1} back to main-context
        sink(1); // push {1} back to main-context
        sink(2); // push {2} back to main-context
        sink(3); // push {3} back to main-context
        sink(5); // push {5} back to main-context
        sink(8); // push {8} back to main-context
    });
while(source){            // test if pull-coroutine is valid
    int ret=source.get(); // access data value
    source();             // context-switch to coroutine-function
}
passing data from main-context to a push-coroutine

Для передачи данных вкорутин<>::push_typeиз основного контекста фреймворк синтезирует.coroutine<>::pull_type, связанный сcoroutine<>::push_typeв основном контексте. Синтезированныйкорутин<>::pull_typeпередается в качестве аргументакорутин-функция. Основной контекст должен называть этоcoroutine<>::push_type::operator(), чтобы передать каждое значение данных в.корутин-функция. Доступ к передаваемым данным предоставляется поcoroutine<>::pull_type::get().

typedef boost::coroutines2::coroutine<int>   coro_t;
coro_t::push_type sink( // constructor does NOT enter coroutine-function
    [&](coro_t::pull_type& source){
        for (int i:source) {
            std::cout << i <<  " ";
        }
    });
std::vector<int> v{1,1,2,3,5,8,13,21,34,55};
for( int i:v){
    sink(i); // push {i} to coroutine-function
}
accessing parameters

К параметрам, возвращаемым изкорутинной функции, можно получить доступ с помощьюкорутина<>::pull_type::get().

Разделение доступа к параметрам из функции переключателя контекста позволяет проверить, действителен ликорутин<>::pull_typeпосле возвращения из.coroutine<>::pull_type::operator(), напримерcoroutine<>::pull_typeимеет значения иcoroutine-функцияне прекращена.

typedef boost::coroutines2::coroutine<boost::tuple<int,int>> coro_t;
coro_t::push_type sink(
    [&](coro_t::pull_type& source){
        // access tuple {7,11}; x==7 y==1
        int x,y;
        boost::tie(x,y)=source.get();
    });
sink(boost::make_tuple(7,11));
exceptions

Исключение, брошенное внутрикорутина<>::pull_type'sкорутин-функцияперед его первым вызовомcoroutine<>::push_type::operator()будет повторно брошен конструкторомcoroutine<>::pull_type. Послеcoroutine<>::pull_type's первого вызоваcoroutine<>::push_type::operator(), любое последующее исключение внутри этогоcoroutine-functionбудет повторно брошеноcoroutine<>::pull_type:::pull_type::get().

Исключение, брошенное внутрьcoroutine<>::push_type'scoroutine-функциябудет переброшенаcoroutine<>::push_type::operator().

[Important] Important

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

try {
    // code that might throw
} catch(const boost::coroutines2::detail::forced_unwind&) {
    throw;
} catch(...) {
    // possibly not re-throw pending exception
}
[Important] Important

Не прыгайте из ловушки и не выбрасывайте исключение в другом контексте исполнения.

Stack unwinding

Иногда необходимо раскручивать стек незавершенного корутина, чтобы уничтожить локальные переменные стека, чтобы они могли высвобождать выделенные ресурсы (паттерн RAII). Аргумент<attributes>корутинного конструктора указывает, должен ли деструктор разматывать стек (стек разматывается по умолчанию).

Stack unwinding предполагает следующие предварительные условия:

  • Корутина нене-корутина
  • Коротин не является полным
  • Коротин не работает
  • Коротин владеет стеком

После раскручивания завершаетсякорутина.

struct X {
    X(){
        std::cout<<"X()"<<std::endl;
    }
    ~X(){
        std::cout<<"~X()"<<std::endl;
    }
};
{
    typedef boost::coroutines2::coroutine<void>::push_type   coro_t;
    coro_t::push_type sink(
        [&](coro_t::pull_type& source){
            X x;
            for(int=0;;++i){
                std::cout<<"fn(): "<<i<<std::endl;
                // transfer execution control back to main()
                source();
            }
        });
    sink();
    sink();
    sink();
    sink();
    sink();
    std::cout<<"sink is complete: "<<std::boolalpha<<!sink<<"\n";
}
output:
    X()
    fn(): 0
    fn(): 1
    fn(): 2
    fn(): 3
    fn(): 4
    fn(): 5
    sink is complete: false
    ~X()
Range iterators

Boost.Coroutine2обеспечивает выход-и-вход-итераторы с использованием __boost_range__.coroutine<>::pull_typeможет использоваться с помощью вход-итераторов с использованиемstd::begin()иstd::end().

typedef boost::coroutines2::coroutine< int > coro_t;
int number=2,exponent=8;
coro_t::pull_type source(
    [&](coro_t::push_type & sink){
        int counter=0,result=1;
        while(counter++<exponent){
            result=result*number;
            sink(result);
        }
    });
for (auto i:source)
    std::cout << i <<  " ";
output:
    2 4 8 16 32 64 128 256

coroutine<>::pull_type::iterator::operator++()соответствуетcoroutine<>::pull_type::operator();coroutine<>::pull_type::iterator::operator*()примерно соответствуетcoroutine<>::pull_type::get(). Итератор, первоначально полученный изstd::begin()coroutine<>::pull_type, сравнивается с итератором, полученным изstd::end()того жеcoroutine<>::pull_type, когда егоcoroutine<>::pull_type::operator boolвозвращался бы<false>.

[Note] Note

Если<T>является только движущимся типом, тоcoroutine::pull_type::iteratorможет быть отменен только один раз, прежде чем он будет увеличен снова.

Производители могут быть созданы изcoroutine<>::push_type.

typedef boost::coroutines2::coroutine<int>   coro_t;
coro_t::push_type sink(
    [&](coro_t::pull_type& source){
        while(source){
            std::cout << source.get() <<  " ";
            source();
        }
    });
std::vector<int> v{1,1,2,3,5,8,13,21,34,55};
std::copy(begin(v),end(v),begin(sink));

coroutine<>::push_type::iterator::operator*()примерно соответствуетcoroutine<>::push_type::operator(). Итератор, первоначально полученный изstd::begin()изкорутина<>::push_typeсравнивается с итератором, полученным изstd::end()того жеcoroutine<>::push_type, когда егоcoroutine<>::push_type::operator boolвернется<false>.

Exit a coroutine-function

корутин-функциявыводится с простым заявлением о возврате, возвращающимся к рутине вызова.coroutine<>::pull_type,coroutine<>::push_typeстановится полным, напримерcoroutine<>::pull_type::operator bool,coroutine<>::push_type::operator boolвернется<false>.

[Important] Important

После возвращения изкорутин-функциизавершенакорутина(не может быть возобновлена скорутина<>::push_type::operator(),корутина<>::pull_type::operator().


PrevUpHomeNext

Статья Asymmetric coroutine раздела Chapter 1. Coroutine2 Coroutine может быть полезна для разработчиков на c++ и boost.




Материалы статей собраны из открытых источников, владелец сайта не претендует на авторство. Там где авторство установить не удалось, материал подаётся без имени автора. В случае если Вы считаете, что Ваши права нарушены, пожалуйста, свяжитесь с владельцем сайта.



:: Главная :: Coroutine ::


реклама


©KANSoftWare (разработка программного обеспечения, создание программ, создание интерактивных сайтов), 2007
Top.Mail.Ru

Время компиляции файла: 2024-08-30 11:47:00
2025-05-19 20:48:07/0.013646125793457/1