我知道在 Rust 中,异步函数不需要在堆上分配。在 Rust 中,
async
函数返回编译器生成的结构,您可以在该结构上调用编译器生成的 poll
方法(简化)。 Rust 中的异步设计看起来逻辑清晰。
现在让我们看看C++。在我主观看来,C++ 中协程的设计很奇怪。本质上,它迫使您在堆上进行分配,因为您没有其他方法可以使用
co_return
返回某些内容
(我知道可以创建一个自定义分配器,在堆栈上的缓冲区中进行分配,但在我看来,这会使代码不必要地复杂化)
我的问题:
promise_type
?为什么await_ready
、await_suspend
、await_resume
还不够? (看起来很奇怪,这就是强制你进行分配的原因,你不能直接构造 SomeTask<T>
对象(使用三个 await_*
方法)并返回它)提前谢谢您
本质上,它迫使您在堆上进行分配,因为您没有其他方法可以使用
返回某些内容co_return
这不是分配的原因。
协程需要存储来完成其工作。不仅仅是返回值的编组,而是用于跟踪整个事情。协程的堆栈,即必须暂停和恢复的东西,就是其中的一部分。 协程的全部意义在于它可以暂停执行并由某人恢复执行。这个人是谁并不能静态地获知。这就是为什么需要动态分配的原因;协程的生命周期不受其初始调用者范围的限制。
如果函数 X 调用协程 A,并且协程 A 挂起自身以等待某些异步请求,则函数 X 可能会将协程返回给其他等待它的人。这意味着函数 X 的调用堆栈消失了。如果协程 A 存在于函数 X 的堆栈上,那么它的存储空间现在就消失了。
这很糟糕。
C++ 将协程 A 存储在动态分配的内存中,以
允许它在调用中存活。这是协程的假定默认情况。 据推测,您所描述的情况是函数 X 本身是一个等待 A 的协程,或者在 A 完成之前不会离开自己的作用域。这将允许 A 的协程状态毫无问题地存在于 X 的堆栈上。如果检测到这些条件,C++ 编译器可以对其进行优化;他们可以省略动态分配。
但除此之外,动态分配是
有必要的。 我不了解 Rust。所以我不知道 Rust 的
async
功能在做什么。也许它的异步函数无法持续超出其调用范围的边界。也许 Rust 能够更好地检测异步函数何时
需要能够超出其调用者的范围。也许您必须明确告诉 Rust 当您希望协程逃脱其调用者的作用域时。我不知道。 但 C++ 不是那样的。 C++ 协程被假定
离开其调用函数的范围。