下面的代码没有达到我的预期。
#include <iostream>
#include <coroutine>
#include <vector>
struct symmetic_awaitable
{
std::coroutine_handle<> _next_h;
symmetic_awaitable(std::coroutine_handle<> h) : _next_h(h) {}
constexpr bool await_ready() const noexcept { return false; }
std::coroutine_handle<> await_suspend(std::coroutine_handle<>) const noexcept
{
return _next_h;
}
constexpr void await_resume() const noexcept {}
};
struct return_object : std::coroutine_handle<>
{
struct promise_type
{
return_object get_return_object()
{
return std::coroutine_handle<promise_type>::from_promise(*this);
}
std::suspend_always initial_suspend() noexcept { return {}; }
std::suspend_always final_suspend() noexcept { return {}; }
void return_void() {}
void unhandled_exception() {}
};
return_object(std::coroutine_handle<promise_type> h) : std::coroutine_handle<>(h) {}
};
std::vector<return_object> co_list;
return_object fa()
{
std::cout << "fa1" << std::endl;
co_await symmetic_awaitable(co_list[1]);
std::cout << "fa2" << std::endl;
co_return;
}
return_object fb()
{
std::cout << "fb1" << std::endl;
co_await symmetic_awaitable(co_list[2]);
std::cout << "fb2" << std::endl;
co_await std::suspend_always{};
std::cout << "fb3" << std::endl;
co_return;
}
return_object fc()
{
std::cout << "fc1" << std::endl;
co_await symmetic_awaitable(co_list[1]);
std::cout << "fc2" << std::endl;
co_return;
}
int main()
{
auto a = fa();
auto b = fb();
auto c = fc();
co_list.push_back(a);
co_list.push_back(b);
co_list.push_back(c);
a.resume();
std::cout << "end" << std::endl;
a.destroy();
b.destroy();
c.destroy();
}
我认为输出将是
fa1
fb1
fc1
fb2
fa2
end
但实际输出是
fa1
fb1
fc1
fb2
end
然后我用co_list[i].resume
替换所有的co_await symmetic_awaitable(co_list[i])
。输出很奇怪
fa1
fb1
fc1
fb1
fc1
..... // the following is infinite loop of "fb1 fc1"
.....
.....
c++ 20协程隐藏了太多的细节,以至于代码不能像我期望的那样正常工作,除非我清楚地知道它们。
阅读cppreference后,我的问题如下:
1。"caller"one_answers"caller"有什么区别?和";resumer"?
a调用b.resume()
,则a是b的接收方还是调用方?
2。"暂停"的确切含义是什么??
a呼叫b.resume()
,则a挂起或正在运行?a通过co_await
恢复b,则a挂起或正在运行?
如果一个函数是协程,它只能以下列方式之一挂起:
- 协程启动时,如果promise最初挂起。
- 当直接调用
co_await
表达式(或等价的,如co_yield
)时。 - 当协程
co_return
s时,如果promise最终挂起。
没有其他可以导致协程挂起。"co"在"coroutine"代表合作多任务。这意味着没有多任务处理,除非涉及的函数合作。明确。
你对你期望代码如何工作的假设似乎表明你相信协程有某种执行堆栈。当await_suspend
被调用时,当前的协程被放到一个堆栈中,当你返回的协程句柄以某种方式完成时,这个堆栈将被弹出。因此,当您调用co_await std::suspend_always{};
时,这将恢复先前挂起的协程。
这些都不是真的。除非你自己制造机器。
协程系统只做你让它做的事情。
调用a.resume()
后立即调用堆栈如下所示:
main()
fa()
当fa
挂起并恢复fb
时,现在看起来像这样:
main()
fb()
fa
isgone。你暂停了它。它不再位于调用堆栈上。只有当你明确地请求恢复时,它才会被恢复。
如果你想让fa
的暂停进入fb
意味着fa
将在fb
结束后继续运行,那么你必须将构建到你的协同程序机器中。这不是偶然发生的;你有责任去实现它。
您的await_suspend
代码需要接受它给出的句柄(指fa
)并将其存储在一个地方,当fb
完成时,它可以恢复fa
。这通常会在fb
的promise对象中,这样final_suspend
就可以恢复它(通常会传递fb
生成的数据)。请记住:无论promise的final_suspend
返回什么,最终挂起点都将返回co_await
,因此您可以只返回想要恢复的协程的句柄。
我不知道那是什么意思。我怀疑你是在问caller"有什么区别?和";resumer">
?
co_await
和直接调用coroutine_handle::resume
函数之间的区别是什么。
如前所述,在初始和最终挂起点之外,只有co_await
(或等价的)表达式可以导致协程挂起。在句柄上调用resume
就像在函数的中间调用一样。它就像任何其他函数调用一样工作;它进入堆栈,等等
让co_await
恢复协程是不同的。当await_suspend
返回一个协程句柄时,这个用调用堆栈上恢复的协程替换您的协程。这就是挂起协程的意义所在。