Boost :: Coroutine2和CoroutineTS(C ++ 20)是C ++中流行的协程实现。两者都暂停和恢复,但是两种实现遵循完全不同的方法。
CoroutineTS(C ++ 20)
generator<int> Generate()
{
co_yield;
});
boost :: coroutine2
pull_type source([](push_type& sink)
{
sink();
});
是否有任何我只应选择其中一种的特定用例?
答案 0 :(得分:3)
主要的技术区别是您是否希望能够从嵌套调用中屈服。使用无堆栈协程无法做到这一点。
要考虑的另一件事是,堆栈式协程具有自己的堆栈和上下文(例如信号掩码,堆栈指针,CPU寄存器等),因此它们比无堆栈的协程具有更大的内存占用。这可能是个问题,特别是如果您有一个资源受限的系统或大量同时存在的协程。
我不知道它们如何在现实世界中比较性能,但是总的来说,无栈协程效率更高,因为它们的开销较小(无栈任务切换不必交换栈,存储/加载寄存器和恢复信号掩码等。
有关最小无堆栈协程实现的示例,请参见使用Simon Tatham's coroutines的Duff's Device。很直观,它们尽可能地高效。
此外,this question给出了很好的答案,它们详细介绍了堆栈式协程和非堆栈式协程之间的区别。
如何从无堆栈协程中的嵌套调用中获得收益? 即使我说不可能,也不是100%正确:您可以使用(至少两个)技巧来实现这一点,每个技巧都有一些缺点: 首先,您必须将应该能够产生调用协程的每个调用也转换为协程。现在,有两种方法:
蹦床方法:您只需循环调用父协程中的子协程,直到返回为止。每次您通知子协程时,如果未完成,您也会产生调用协程。请注意,此方法禁止直接调用子协程,您始终必须调用最外部的协程,然后必须重新输入整个调用堆栈。嵌套深度 n 的调用和返回复杂度为 O(n)。如果您正在等待事件,则该事件只需通知最外部的协程。
父链接方法:将父协程地址传递给子协程,生成父协程,子协程完成后手动恢复父协程。请注意,此方法禁止直接调用除最内部协程之外的任何协程。这种方法的调用和返回复杂度为 O(1),因此通常更可取。缺点是您必须在某个位置手动注册最内部的协程,以便下一个要恢复外部协程的事件知道直接定位哪个内部协程。
注意:通过调用和返回复杂度,我的意思是通知协程以恢复协程时所采取的步骤数,以及通知协程返回至协程后所采取的步骤数。再次调用通知程序。