7

Boost::Coroutine2 和 CoroutineTS(C++20) 是 C++ 中流行的协程实现。两者都暂停和恢复,但两种实现遵循完全不同的方法。

协程TS(C++20)

  • 无堆叠
  • 通过返回暂停
  • 使用特殊关键字
generator<int> Generate()
{
   co_yield;
});

升压::协程2

  • 堆积如山
  • 呼叫暂停
  • 不要使用特殊关键字
pull_type source([](push_type& sink)
{
   sink();
});

有没有我应该只选择其中一个的特定用例?

4

1 回答 1

5

主要的技术区别是您是否希望能够从嵌套调用中产生。使用无堆栈协程无法做到这一点。

另一件需要考虑的事情是,堆栈式协程具有自己的堆栈和上下文(例如信号掩码、堆栈指针、CPU 寄存器等),因此它们比无堆栈协程具有更大的内存占用。这可能是一个问题,特别是如果您有一个资源受限的系统或同时存在大量协程。

我不知道它们在现实世界中如何比较性能,但总的来说,无堆栈协程更有效,因为它们的开销更少(无堆栈任务切换不必交换堆栈、存储/加载寄存器和恢复信号面具等)。

有关最小无堆栈协程实现的示例,请参阅Simon Tatham 的使用Duff 的设备的协程。很直观的是,它们尽可能高效。

此外,这个问题有很好的答案,更详细地介绍了堆栈协程和无堆栈协程之间的差异。

如何从无堆栈协程中的嵌套调用中产生?  尽管我说不可能,但这并不是 100% 正确的:您可以使用(至少两个)技巧来实现这一点,每个技巧都有一些缺点:首先,您必须转换应该能够产生调用协程的每个调用也进入协程。现在,有两种方法:

  1. 蹦床方法:您只需在循环中从父协程调用子协程,直到它返回。每次你通知子协程,如果它没有完成,你也会让调用协程。请注意,这种方法禁止直接调用子协程,您始终必须调用最外层的协程,然后必须重新进入整个调用堆栈。这对于嵌套深度n具有O(n)的调用和返回复杂度。如果您正在等待一个事件,该事件只需通知最外层的协程即可。

  2. 父链接方式:你把父协程地址传给子协程,yield父协程,子协程完成后手动恢复父协程。请注意,这种方法禁止直接调用除最内层协程之外的任何协程。这种方法的调用和返回复杂度为O(1),因此通常更可取。缺点是你必须在某个地方手动注册最里面的协程,以便下一个想要恢复外部协程的事件知道直接针对哪个内部协程。

注意调用和返回复杂度是指通知协程恢复它时所采取的步骤数,以及通知它再次返回调用通知程序后所采取的步骤。

于 2019-03-09T17:28:22.157 回答