Как вы реализуете сопрограммы в C ++ - PullRequest
62 голосов
/ 23 сентября 2008

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

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

Ответы [ 17 ]

2 голосов
/ 12 августа 2014

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

2 голосов
/ 04 ноября 2010

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

1 голос
/ 05 января 2017

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

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

1 голос
/ 16 ноября 2016

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

0 голосов
/ 23 сентября 2008

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

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

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

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

0 голосов
/ 23 сентября 2008

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

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

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

0 голосов
/ 27 мая 2012

Я пытался реализовать сопрограммы самостоятельно, используя 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();
    }
}
...