Как можно реализовать сопрограммы в языке C++



Я сомневаюсь, что это можно сделать переносимо, но есть ли какие-либо решения? Я думаю,что это можно сделать, создав альтернативный стек и сбросив SP, BP и IP при входе в функцию, а также получив выход сохранить IP и восстановить SP+BP. Деструкторы и безопасность исключений кажутся сложными, но разрешимыми.



Это было сделано? Это невозможно?

813   17  

17 ответов:

да можно сделать без проблем. Все, что вам нужно, это немного кода сборки для перемещения стека вызовов в недавно выделенный стек в куче.

Я посмотреть boost:: coroutine библиотека.

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

в POSIX можно использовать подпрограммы makecontext()/swapcontext() для переносимого переключения контекстов выполнения. На Windows, вы можете использовать API волокна. В противном случае все, что вам нужно, это немного кода сборки клея, который переключает контекст машины. Я реализовал сопрограммы как с ASM (для AMD64), так и с swapcontext (); ни один из них не очень жесткий.

для потомков,

Дмитрий Вьюков wondeful web site имеет умный трюк с использованием ucontext и setjump для моделирования сопрограмм в c++.

кроме того, контекстная библиотека Оливера Ковальке была в последнее время принято в Boost, поэтому, надеюсь, мы увидим обновленную версию boost.сопрограмма, которая работает на x86_64 в ближайшее время.

нет простого способа реализовать сопрограмму. Потому что сам coroutine находится вне абстракции стека C/C++, как и поток. Таким образом, он не может поддерживаться без изменения уровня языка для поддержки.

В настоящее время (C++11) все существующие реализации сопрограмм C++ основаны на взломе уровня сборки, который трудно обеспечить безопасным и надежным пересечением платформ. Чтобы быть надежным, он должен быть стандартным и обрабатываться компиляторами, а не взломом.

там стандартное предложение-N3708 для этого. Проверьте это, если вам интересно.

возможно, вам лучше использовать итератор, чем сопрограмму, если это возможно. Таким образом, вы можете продолжать звонить next() чтобы получить следующее значение, но вы можете сохранить свое состояние в качестве переменных-членов вместо локальных переменных.

Это может сделать вещи более ремонтопригодны. Другой разработчик C++ может не сразу понять сопрограмму, тогда как они могут быть более знакомы с итератором.

Я не думаю, что есть много полноценных, чистых реализаций в C++. Одна попытка, что мне нравится это protothread библиотека.

тут сопрограмма портативная библиотека C++ для сопрограмма последовательности указать вам в правильном направлении? Это похоже на элегантное решение, которое продлилось испытание time.....it ей 9 лет!

в папке DOC находится pdf-файл документа портативная библиотека C++ для Сопрограммирования Keld Helsgaun, которая описывает библиотеку и предоставляет короткие примеры ее использования.

[обновление] я на самом деле успешно использую его сам. Любопытство взяло лучше меня, поэтому я изучил это решение и обнаружил, что оно хорошо подходит для проблемы, над которой я работал в течение некоторого времени!

для тех, кто хочет знать, как они могут использовать сопрограммы портативным способом в C++ вам придется ждать C++17. Комитет по стандартам работает над этой функцией см. бумага N3722. Чтобы обобщить текущий проект документа, вместо Async и Await ключевые слова будут возобновляться и ждать.

взгляните на экспериментальную реализацию в Visual Studio 2015, чтобы играть с экспериментальной реализацией Microsoft. Это не похоже у clang еще есть реализация.

есть хороший разговор от Cppcon соизмеряет отрицательную абстракцию накладных расходов описать преимущества использования сопрограмм в C++ и как это влияет на простоту и производительность кода.

в настоящее время мы все еще должны использовать реализации библиотеки, но в ближайшем будущем у нас будут сопрограммы в качестве основной функции C++.

обновление: Похоже, что реализация сопрограммы не делает ее в C++17, но она будет быть техническим заданием (p0057r2). С другой стороны, похоже, что их поддержка находится в clang с флагом-fcoroutines_ts и в Visual Studio 2015 Update 2. Ключевые слова также имеют co_ присоединяется к ним. Так что co_await, co_yield и т. д.

новая библиотека, импульс.Контекст, был выпущен сегодня с портативными функциями для реализации сопрограмм.

Это старый поток, но я хотел бы предложить взломать устройство Даффа, которое не зависит от ос (насколько я помню):

C сопрограммы с использованием устройства Даффа

и в качестве примера, вот библиотека telnet, которую я изменил, чтобы использовать сопрограммы вместо fork / threads: библиотека cli Telnet с использованием сопрограмм

и поскольку стандартный C до C99 по существу является истинным подмножеством C++, это хорошо работает и в C++.

Он основан на макросах (cringe), но следующий сайт предоставляет простую в использовании реализацию генератора:http://www.codeproject.com/KB/cpp/cpp_generators.aspx

Я придумал реализацию без asm код. Идея состоит в том, чтобы использовать функцию создания потока системы для инициализации стека и контекста и использовать setjmp/longjmp для переключения контекста. Но это не портативный, см. tricky pthread version если вы заинтересованы.

https://github.com/tonbit/coroutine - это c++11 single .H асимметричная реализация сопрограммы, поддерживающая примитивы resume/yield/await и модель канала. Он реализуется через ucontext/fiber, не зависящий от boost, работающий на linux/windows / macOS. Это хорошая отправная точка для изучения реализации сопрограммы в c++.

Проверьте мою реализацию, она иллюстрирует точку взлома asm и проста:

https://github.com/user1095108/generic/blob/master/coroutine.hpp

вы всегда должны рассматривать использование потоков вместо этого; особенно в современном оборудовании. Если у вас есть работа, которая может быть логически разделена в совместных подпрограммах, использование потоков означает, что работа может фактически выполняться одновременно отдельными исполнительными блоками (процессорными ядрами).

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

Если вы работаете в Windows, вы должны взглянуть на волокнами. Волокна подарит вам сопрограмма-как основа при поддержке операционной системы.

Я не знаком с другими ОС, чтобы рекомендовать альтернативы там.

WvCont является частью WvStreams который реализует так называемые полу-сопрограммы. С ними немного легче справиться, чем с полными сопрограммами: вы вызываете его, и он возвращается к человеку, который его вызвал.

он реализован с использованием более гибкой WvTask, которая поддерживает полнофункциональные сопрограммы; вы можете найти его в той же библиотеке.

работает на win32 и Linux, по крайней мере, и, вероятно, любой другой системе Unix.

Я пытался реализовать сопрограммы сам с помощью C++11 и потоков:

#include <iostream>
#include <thread>

class InterruptedException : public std::exception {
};

class AsyncThread {
public:
    AsyncThread() {
        std::unique_lock<std::mutex> lock(mutex);
        thread.reset(new std::thread(std::bind(&AsyncThread::run, this)));
        conditionVar.wait(lock); // wait for the thread to start
    }
    ~AsyncThread() {
        {
            std::lock_guard<std::mutex> _(mutex);
            quit = true;
        }
        conditionVar.notify_all();
        thread->join();
    }
    void run() {
        try {
            yield();
            for (int i = 0; i < 7; ++i) {
                std::cout << i << std::endl;
                yield();
            }
        } catch (InterruptedException& e) {
            return;
        }
        std::lock_guard<std::mutex> lock(mutex);
        quit = true;
        conditionVar.notify_all();
    }
    void yield() {
        std::unique_lock<std::mutex> lock(mutex);
        conditionVar.notify_all();
        conditionVar.wait(lock);
        if (quit) {
            throw InterruptedException();
        }
    }
    void step() {
        std::unique_lock<std::mutex> lock(mutex);
        if (!quit) {
            conditionVar.notify_all();
            conditionVar.wait(lock);
        }
    }
private:
    std::unique_ptr<std::thread> thread;
    std::condition_variable conditionVar;
    std::mutex mutex;
    bool quit = false;
};

int main() {
    AsyncThread asyncThread;
    for (int i = 0; i < 3; ++i) {
        std::cout << "main: " << i << std::endl;
        asyncThread.step();
    }
}

Comments

    Ничего не найдено.