是否可以在C++中制作零分配协程运行时?

问题描述 投票:0回答:1

我知道在 Rust 中,异步函数不需要在堆上分配。在 Rust 中,

async
函数返回编译器生成的结构,您可以在该结构上调用编译器生成的
poll
方法(简化)。 Rust 中的异步设计看起来逻辑清晰。

现在让我们看看C++。在我主观看来,C++ 中协程的设计很奇怪。本质上,它迫使您在堆上进行分配,因为您没有其他方法可以使用

co_return

返回某些内容

(我知道可以创建一个自定义分配器,在堆栈上的缓冲区中进行分配,但在我看来,这会使代码不必要地复杂化)

我的问题:

  1. (关于C++设计决策)为什么在C++中,协程返回的对象必须有一个
    promise_type
    ?为什么
    await_ready
    await_suspend
    await_resume
    还不够? (看起来很奇怪,这就是强制你进行分配的原因,你不能直接构造
    SomeTask<T>
    对象(使用三个
    await_*
    方法)并返回它)
  2. 是否可以在没有自定义分配器的情况下制作零分配协程?

提前谢谢您

c++ asynchronous c++20 language-design c++-coroutine
1个回答
0
投票

本质上,它迫使您在堆上进行分配,因为您没有其他方法可以使用

co_return

返回某些内容

这不是分配的原因。

协程需要存储来完成其工作。不仅仅是返回值的编组,而是用于跟踪整个事情。协程的堆栈,即必须暂停和恢复的东西,就是其中的一部分。 协程的全部意义在于它可以暂停执行并由某人恢复执行。这个人是谁并不能静态地获知。这就是为什么需要动态分配的原因;协程的生命周期不受其初始调用者范围的限制。

如果函数 X 调用协程 A,并且协程 A 挂起自身以等待某些异步请求,则函数 X 可能会将协程返回给其他等待它的人。这意味着函数 X 的调用堆栈消失了。如果协程 A 存在于函数 X 的堆栈上,那么它的存储空间现在就消失了。

这很糟糕。

C++ 将协程 A 存储在动态分配的内存中,以

允许

它在调用中存活。这是协程的假定默认情况。 据推测,您所描述的情况是函数 X 本身是一个等待 A 的协程,或者在 A 完成之前不会离开自己的作用域。这将允许 A 的协程状态毫无问题地存在于 X 的堆栈上。如果检测到这些条件,C++ 编译器可以对其进行优化;他们可以省略动态分配。

但除此之外,动态分配是

有必要的

我不了解 Rust。所以我不知道 Rust 的

async

功能在做什么。也许它的异步函数无法持续超出其调用范围的边界。也许 Rust 能够更好地检测异步函数何时

需要
能够超出其调用者的范围。也许您必须明确告诉 Rust 当您希望协程逃脱其调用者的作用域时。我不知道。 但 C++ 不是那样的。 C++ 协程被

假定

离开其调用函数的范围。

© www.soinside.com 2019 - 2024. All rights reserved.