题
我怀疑它可以移植,但有没有解决方案?我认为可以通过创建备用堆栈并在功能输入上重置SP,BP和IP,并使产量保存IP并恢复SP + BP来完成。析构函数和异常安全似乎很棘手,但可以解决。
它已经完成了吗?这不可能吗?
解决方案
是的可以完成没有问题。您只需要一个小的汇编代码来将调用堆栈移动到堆上新分配的堆栈。
我会查看 boost :: coroutine 库。
你应该注意的一件事是堆栈溢出。在大多数操作系统上,溢出堆栈将导致段错误,因为未映射虚拟内存页。但是,如果您在堆上分配堆栈,则无法获得任何保证。 请记住这一点。
其他提示
在POSIX上,您可以使用makecontext()/ swapcontext()例程来可移植地切换执行上下文。在Windows上,您可以使用光纤API。否则,您只需要一些胶水装配代码来切换机器上下文。我已经使用ASM(对于AMD64)和swapcontext()实现了协同程序;既不是很难。
没有简单的方法来实现协同程序。由于协程本身不像C / C ++的堆栈抽象,就像线程一样。因此,如果没有语言级别的更改,就无法支持它。
目前(C ++ 11),所有现有的C ++协程实现都基于程序集级别的黑客攻击,难以跨越平台安全可靠。为了可靠,它需要是标准的,并由编译器处理而不是黑客攻击。
标准提案 - N3708 为此。如果您有兴趣,请查看它。
如果可能的话,使用迭代器可能比使用协程更好。这样你可以继续调用 next()
来获取下一个值,但你可以将你的状态保存为成员变量而不是局部变量。
它可能使事情更易于维护。另一个C ++开发人员可能不会立即理解协程,而他们可能更熟悉迭代器。
对于那些想要了解他们如何在C ++中以可移植的方式利用Coroutines的人̶ o̶ u̶ ̶ W̶ I̶ L̶ L̶ ̶ H̶ A̶ V̶ E̶ ̶ T̶ O̶ ̶ W̶ A̶ I̶ T̶ ̶ F̶ O̶ R̶ ̶ C̶ +̶ +̶ 1̶ 7̶等待结束了(见下文)!标准委员会正在研究该功能,请参阅 N3722论文。总结本文的当前草案,而不是Async和Await,关键字将是可恢复的,等待。
看一下Visual Studio 2015中的实验性实现,以便与Microsoft的实验性实现一起使用。看起来clang还没有实现。
来自Cppcon的一个很好的演讲 Coroutines负面开销抽象概述了使用Coroutines的好处C ++以及它如何影响代码的简单性和性能。
目前我们仍然需要使用库实现,但在不久的将来,我们将使用协同程序作为核心C ++特性。
更新: 看起来协程实现是针对C ++ 20的,但是作为C ++ 17的技术规范发布( p0057r2 )。 Visual C ++,clang和gcc允许您选择使用编译时标志。
用于协同序列的可移植C ++库的COROUTINE 指向您在正确的方向?它似乎是一个优雅的解决方案,经历了时间的考验.....它已经9岁了!
在DOC文件夹中是Keld Helsgaun撰写的用于协同序列的便携式C ++库的pdf文件,其中描述了该库并提供了使用它的简短示例。
[更新]我实际上是自己成功使用它。好奇心让我变得更好,所以我调查了这个解决方案,发现它非常适合我已经工作了一段时间的问题!
我不认为在C ++中有很多完整的,干净的实现。我喜欢的一个尝试是 Adam Dunkels的protothread库。
另见 Protothreads:简化内存受限嵌入式系统的事件驱动编程在ACM数字图书馆和维基百科主题 Protothread 中进行讨论,
新图书馆 Boost .Context ,今天发布了用于实现协同程序的便携式功能。
这是一个旧线程,但我想建议使用Duff的设备,而不依赖于OS(据我记得):
使用Duff设备 C coroutines
作为一个例子,这里是一个telnet库我修改为使用协程而不是fork / threads: 使用协同程序的Telnet cli库
由于C99之前的标准C本质上是C ++的真正子集,因此在C ++中也能很好地工作。
它基于(cringe)宏,但是以下站点提供了一个易于使用的生成器实现: http://www.codeproject.com/KB/cpp/cpp_generators.aspx
我想出了一个实现没有asm 代码。我们的想法是使用系统的线程创建函数来初始化堆栈和上下文,并使用setjmp / longjmp来切换上下文。但它不便携,如果您有兴趣,请参阅棘手的pthread版本。
https://github.com/tonbit/coroutine 是C ++ 11单曲.h支持resume / yield / await primitives和Channel模型的非对称协程实现。它是通过ucontext / fiber实现的,不依赖于boost,在linux / windows / macOS上运行。这是学习在c ++中实现协同程序的一个很好的起点。
查看我的实现,它说明了asm黑客攻击点并且很简单:
https://github.com/user1095108/generic/blob/master /coroutine.hpp
你应该总是考虑使用线程;特别是在现代硬件中。如果你的工作可以在协同例程中进行逻辑分离,那么使用线程意味着工作实际上可以由不同的执行单元(处理器核心)同时完成。
但是,也许你确实想要使用协同程序,也许是因为你有一个经过良好测试的算法,这个算法已经以这种方式编写和测试,或者因为你正在移植以这种方式编写的代码。
如果你在Windows中工作,你应该看一下光纤一>。纤维将在操作系统的支持下为您提供类似协程的框架。
我不熟悉其他操作系统,以推荐替代品。
我尝试使用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();
}
}