你如何在 C 中实现协程

新手上路,请多包涵

我怀疑它可以便携地完成,但有什么解决方案吗?我认为可以通过创建一个备用堆栈并在函数入口处重置 SP、BP 和 IP,并让产量保存 IP 并恢复 SP+BP 来完成。析构函数和异常安全似乎很棘手,但可以解决。

已经完成了吗?这是不可能的吗?

原文由 Mike Elkins 发布,翻译遵循 CC BY-SA 4.0 许可协议

阅读 774
2 个回答

是的,它 可以毫无问题地完成。您只需要一点汇编代码就可以将调用堆栈移动到堆上新分配的堆栈。

我会 看看 boost::coroutine library

您应该注意的一件事是堆栈溢出。在大多数操作系统上,堆栈溢出将导致段错误,因为未映射虚拟内存页面。但是,如果您在堆上分配堆栈,您将得不到任何保证。要时刻铭记在心。

原文由 Ted 发布,翻译遵循 CC BY-SA 3.0 许可协议

也基于宏(Duff 的设备,完全可移植,参见 http://www.chiark.greenend.org.uk/~sgtatham/coroutines.html )并受 Mark 发布的链接的启发,以下模拟协同进程协作使用事件作为同步机制(与传统的协程/生成器样式略有不同的模型)

 // Coprocess.h
#pragma once
#include <vector>

class Coprocess {
  public:
    Coprocess() : line_(0) {}
    void start() { line_ =  0; run(); }
    void end()   { line_ = -1; on_end(); }
    virtual void run() = 0;
    virtual void on_end() {};
  protected:
    int line_;
};

class Event {
  public:
    Event() : curr_(0) {}

    void wait(Coprocess* p) { waiters_[curr_].push_back(p); }

    void notify() {
        Waiters& old = waiters_[curr_];
        curr_ = 1 - curr_; // move to next ping/pong set of waiters
        waiters_[curr_].clear();
        for (Waiters::const_iterator I=old.begin(), E=old.end(); I != E; ++I)
            (*I)->run();
    }
  private:
    typedef std::vector<Coprocess*> Waiters;
    int curr_;
    Waiters waiters_[2];
};

#define corun()   run() { switch(line_) { case 0:
#define cowait(e) line_=__LINE__; e.wait(this); return; case __LINE__:
#define coend     default:; }} void on_end()

使用示例:

 // main.cpp
#include "Coprocess.h"
#include <iostream>

Event e;
long sum=0;

struct Fa : public Coprocess {
    int n, i;
    Fa(int x=1) : n(x) {}
    void corun() {
        std::cout << i << " starts\n";
        for (i=0; ; i+=n) {
            cowait(e);
            sum += i;
        }
    } coend {
        std::cout << n << " ended " << i << std::endl;
    }
};

int main() {
    // create 2 collaborating processes
    Fa f1(5);
    Fa f2(10);

    // start them
    f1.start();
    f2.start();
    for (int k=0; k<=100; k++) {
        e.notify();
    }
    // optional (only if need to restart them)
    f1.end();
    f2.end();

    f1.start(); // coprocesses can be restarted
    std::cout << "sum " << sum << "\n";
    return 0;
}

原文由 acppcoder 发布,翻译遵循 CC BY-SA 4.0 许可协议

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题