这个问题不是关于C ++ 20中的协程,而是一般的协程。
这些天我正在学习C ++ 20协程。我从Coroutines Introduction了解了有关堆栈式和协程式协程的信息。我也特别想了解更多信息。
这是我对无堆栈协程的理解:
无堆栈协程在运行时确实在调用方的堆栈上具有堆栈。
当其自身挂起时,由于无堆栈协程只能在顶级函数处挂起,因此其堆栈是可预测的,有用的数据将存储在特定区域中。
当它不运行时,它没有堆栈。它带有一个句柄,客户端可以通过该句柄恢复协程。
协程TS指定在为协程帧分配存储空间时调用非数组operator new
。但是,我认为这是不必要的,因此是我的问题。
一些解释/考虑:
将协程的位置放在何处?在最初存储指针的句柄中。
动态分配并不意味着存储在堆上。但是我的目的是忽略对operator new
的调用,无论它如何实现。
来自cppreference:
如果可以,可以优化对new运算符的调用(即使使用了自定义分配器)
协程状态的生存期严格嵌套在调用者的生存期内,并且
在呼叫站点知道协程框架的大小
对于第一个要求,如果协程超过了调用者,则将状态直接存储在句柄中仍然可以。
另一方面,如果调用方不知道大小,如何构成调用operator new
的参数?实际上,我什至无法想象呼叫者在什么情况下不知道大小。
铁锈似乎有不同的实现方式。
答案 0 :(得分:4)
无堆栈协程在运行时确实在调用方的堆栈上具有堆栈。
那是您误会的根源。
基于连续的协程(这是“无栈协程”的意思)是一种协程机制,旨在为一些其他代码提供协程,该协程将在某些异步过程完成后恢复其执行。恢复可能会发生在其他线程中。
这样,就不能假定将其放在“调用方的堆栈上”,因为调用方和安排协程恢复的进程不一定在同一线程中。协程必须能够使调用程序的寿命更长,因此协程的堆栈不能位于调用方的堆栈上(通常。在某些if (array.length ===0 ) {
return array;
}
else {
for (i = 0; i < array.length; i++) {
if (array[i].length < tiny.length) {
tiny = array[i];
}
}
return tiny;
}
}
风格的情况下,可以)。
协程柄代表协程的堆栈。只要该句柄存在,协程的堆栈也将存在。
当它不运行时,它没有堆栈。它带有一个句柄,客户端可以通过该句柄恢复协程。
这个“句柄”如何存储协程的所有局部变量?显然,它们是被保留的(如果没有,那将是一个不好的协程机制),因此必须将它们存储在某个地方。函数的局部变量的命名位置称为“堆栈”。
将其称为“句柄”不会改变它的含义。
但是我的目的是忽略对
co_yield
的调用,无论它如何实现。
嗯...你不能。如果从不调用operator new
是编写所写软件的重要组成部分,则不能使用new
风格的协程延续。没有可以使用的规则集来保证协程中co_await
的省略。如果您使用的是特定的编译器,则可以进行一些测试以查看其隐藏的内容和未隐藏的内容,
您引用的规则仅仅是使可能取消通话的情况。
另一方面,如果调用方不知道大小,如何构成调用
new
的参数?
请记住:C ++中的operator new
协程实际上是函数的实现细节。调用方不知道,它所调用的任何函数是否是协程。从外部看,所有协程看起来像常规函数。
用于创建协程堆栈的代码发生在函数调用中,而不是在函数调用之外。
答案 1 :(得分:2)
考虑这个假设情况:
void foo(int);
task coroutine() {
int a[100] {};
int * p = a;
while (true) {
co_await awaitable{};
foo (*p);
}
}
p
指向a
的第一个元素,如果两次恢复之间a
的存储位置发生了变化,则p
将不会保留正确的地址。>
用于功能栈的内存必须以在暂停和后续恢复之间保存的方式分配。但是,如果某些对象引用了该内存中的对象(或者至少没有增加复杂性),则无法移动或复制该内存。这就是为什么有时编译器需要在堆上分配此内存的原因。
答案 2 :(得分:1)
堆栈式协程和无堆栈式协程之间的根本区别在于,协程是否像线程一样拥有完整的,理论上无界的堆栈(但实际上是有界的)。
在一个堆栈式协程中,协程的局部变量像在执行期间和挂起时一样,存储在它拥有的堆栈中。
在无堆栈协程中,协程运行或不运行时,协程的局部变量可以位于堆栈中。它们存储在无栈协程拥有的固定大小的缓冲区中。
在理论中,可以将无栈协程存储在某人的栈中。但是,无法在C ++代码中保证会发生这种情况。
在创建协程时,省略new运算符就可以做到这一点。如果您的协程对象存储在某人的堆栈上,并且因为协程对象本身具有足够的状态空间而删除了新协程对象,则可能完全生活在其他人的堆栈上的无堆栈协程。
在当前的C ++协程实现中,没有任何方法可以保证这一点。试图在编译器开发人员遇到阻力的情况下实现这一目标,因为协程的确进行的最小捕获发生的时间“晚于”他们需要知道协程中有多大的时间。
这导致实践上的差异。堆栈式协程的行为更像是线程。您可以调用常规函数,这些常规函数可以在其体内与协程操作(例如暂停)相互作用。
无栈协程不能调用函数,然后与协程机器进行交互。仅在无堆栈协程自身内部才允许与协程机器进行交互。
堆栈式协程具有线程的所有机制,而没有在OS上进行调度。无栈协程是一种增强的功能对象,其中带有goto标签,可以使它通过其主体部分恢复。
有一些无栈协程的理论实现,这些实现没有“可以调用新的”功能。 C ++标准不需要这种无堆栈协程。
有人提出了他们。他们的建议输给了当前的建议,部分原因是与其他建议相比,当前的建议更精致,更易于发货。替代提案的某些语法最终以成功的提案结束。
我相信,有一个令人信服的论点,即“限制器”固定大小的无新协程实现在当前提案中并未排除,可以在以后添加,这有助于终止其他提案。