你如何在C ++中实现协程?
我怀疑它可以做到便携,但有没有解决scheme吗? 我认为可以通过创build一个备用堆栈,并在functioninput上重新设置SP,BP和IP,并有产量保存IP和恢复SP + BP来完成。 析构函数和exception安全性似乎很难但可以解决。
它已经完成了吗? 这是不可能的吗?
是的,它可以做到没有问题。 您只需要一个小的汇编代码即可将调用堆栈移动到堆上新分配的堆栈。
我会看看boost :: coroutine库 。
你应该注意的一件事是堆栈溢出。 在大多数操作系统溢出堆栈将导致段错误,因为虚拟内存页面没有映射。 但是,如果您在堆上分配堆栈,则不能得到任何保证。 要时刻铭记在心。
在POSIX上,可以使用makecontext()/ swapcontext()例程来移植执行上下文。 在Windows上,您可以使用光纤API。 否则,你所需要的只是一些切换机器上下文的胶合代码。 我已经用ASM(用于AMD64)和swapcontext()来实现协程。 既不是很难。
对于后人来说,
德米特里·维尤科夫(Dmitry Vyukov)的神奇网站在使用ucontext和setjump在c ++中模拟协程时有一个聪明的技巧。
此外,Oliver Kowalke的上下文库最近也被 Boost所接受 ,因此希望我们能够看到boost.coroutine的更新版本,它很快就能在x86_64上运行。
没有简单的方法来实现协程。 因为协程本身就像线程一样,超出了C / C ++的栈抽象。 所以如果没有语言级别的变化就不能支持。
目前(C ++ 11),所有现有的C ++协程实现都是基于组装级别的黑客攻击,难以跨平台安全可靠。 为了可靠,它需要是标准的,由编译器处理,而不是黑客。
有一个标准的build议 – N3708为此。 如果你有兴趣,请检查一下。
如果可能的话,你可能会用一个迭代器而不是一个协程。 这样你可以继续调用next()
来获得下一个值,但是你可以保持你的状态为成员variables而不是局部variables。
这可能会使事情更易于维护。 另一个C ++开发者可能不会马上理解协程,而他们可能更熟悉迭代器。
我不认为在C ++中有很多完整的,干净的实现。 一个我喜欢的尝试是原始库 。
COROUTINE协程序列的便携式C ++库是否指向正确的方向? 这似乎是一个优雅的解决scheme,持续了时间的考验…..它已经9岁了!
在DOC文件夹中是由Keld Helsgaun提供的用于Coroutine Sequencing的便携式C ++库的pdf文档,其描述了库并提供了使用它的简短示例。
[更新]我实际上是成功地使用它自己。 好奇心越来越好,所以我研究了这个解决scheme,发现它适合我一直在努力的一个问题!
对于那些想知道如何在C ++中以便携方式利用Coroutine的人,你将不得不等待C ++ 17。 标准委员会正在研究该function,参见N3722文件 。 总结本文的草稿,而不是asynchronous和等待,关键字将是可恢复的,并等待。
看看Visual Studio 2015中的实验性实现,以便与微软的实验性实现一起玩。 它看起来不像铿锵有实现。
Cppcon Coroutines有一个很好的讨论, 负面的开销抽象概述了在C ++中使用Coroutines的好处,以及它如何影响代码的简单性和性能。
目前我们仍然需要使用库实现,但在不久的将来,我们将把协程作为核心的C ++特性。
更新:看起来协程的实现不是把它变成C ++ 17,但它是一个技术规范( p0057r2 )。 好的,看起来他们是用-fcoroutines_ts标志和Visual Studio 2015 Update 2中的clang支持的。这些关键字也有一个前缀。 所以co_await,co_yield等
今天发布了一个新的库Boost.Context ,用于实现协同程序的便携function。
这是一个古老的线程,但我想build议一个使用达夫的设备,不依赖于操作系统(据我记得):
C协程使用Duff的设备
作为一个例子,这里是一个telnet库我修改使用协程,而不是fork /线程: Telnet cli库使用协程
由于C99之前的标准C本质上是C ++的一个真正的子集,所以在C ++中也可以很好地工作。
它基于(畏惧)macros,但以下站点提供了一个易于使用的生成器实现: http : //www.codeproject.com/KB/cpp/cpp_generators.aspx
我想出了一个没有ASM代码的实现。 这个想法是使用系统的线程创build函数来初始化堆栈和上下文,并使用setjmp / longjmp切换上下文。 但是这不是可移植的,如果你感兴趣的话,看看棘手的pthread版本 。
https://github.com/tonbit/coroutine是C ++ 11 single .h不对称协程实现,支持resume / yield / await原语和Channel模型。 它通过ucontext / fiber实现,不依赖boost,在linux / windows / macOS上运行。 学习在c ++中实现协程是一个很好的起点。
你应该总是考虑使用线程,而不是; 特别是在现代硬件。 如果在Co-routines中有可以在逻辑上分离的工作,则使用线程意味着该工作可能实际上由单独的执行单元(处理器核心)同时完成。
但是,也许你确实想要使用协程,也许是因为你有一个已经被testing过的algorithm,这个algorithm已经被编写和testing过了,或者你正在移植这样写的代码。
如果你在Windows下工作,你应该看看光纤 。 光纤将给你一个协同操作系统的支持。
我不熟悉其他操作系统在那里推荐替代品。
WvCont是WvStream的一部分,它实现了所谓的半协程。 这些比完整的协程更容易处理:你调用它,然后回到调用它的人。
它使用更灵活的WvTask来实现,它支持完整的协同程序; 你可以在同一个库中find它。
至less在win32和Linux上工作,可能还有其他的Unix系统。
看看我的实现,它说明了asm的黑客点,很简单:
https://github.com/user1095108/generic/blob/master/coroutine.hpp
我试图使用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(); } }