测试代码
#include <coroutine>
#include <iostream>
/**
* @brief 协程特定返回类型
*/
struct task {
struct promise_type {
std::suspend_never initial_suspend() noexcept {
std::cout << " 4, initial_suspend " << std::endl << std::endl;
return {
};
}
std::suspend_never final_suspend() noexcept {
std::cout << "20, final_suspend " << std::endl;
return {
};
}
void unhandled_exception() noexcept {
}
task get_return_object() noexcept {
std::cout << " 2, get_return_object " << std::endl;
auto tk = task{
std::coroutine_handle<promise_type>::from_promise(*this)};
return tk;
}
void return_value(int v) noexcept {
std::cout << "19, return_value: " << v << std::endl << std::endl;
}
std::suspend_always yield_value(std::string&& from) noexcept {
std::cout << "14, yield_value: " << from << std::endl;
value_ = std::move(from);
return {
};
}
std::string value_;
};
std::coroutine_handle<promise_type> coro_;
task(std::coroutine_handle<promise_type> h) : coro_(h) {
std::cout << " 3, task construct " << std::endl;
}
~task() {
std::cout << "22, task deconstruct " << std::endl; }
std::string value() {
return coro_.promise().value_; }
};
/**
* @brief 协程 co_await关键字需要的awaitable
*/
struct awaiter {
awaiter() {
std::cout << " 6, awaiter construct" << std::endl; }
~awaiter() {
std::cout << "11, awaiter deconstruct" << std::endl; }
bool await_ready() noexcept {
std::cout << " 7, await_ready " << std::endl;
return false;
}
void await_resume() noexcept {
std::cout << "10, await_resume " << std::endl;
}
void await_suspend(std::coroutine_handle<>) noexcept {
std::cout << " 8, await_suspend " << std::endl;
}
};
/**
* @brief 协程函数
* @return 协程特定返回类型
*/
task do_by_coroutine() {
std::cout << " 5, before co_await " << std::endl;
co_await awaiter{
};
std::cout << "12, after co_await " << std::endl << std::endl;
std::cout << "13, before co_yield " << std::endl;
co_yield "123";
std::cout << "17, after co_yield " << std::endl << std::endl;
std::cout << "18, before co_return " << std::endl;
co_return 3;
}
int main() {
std::cout << " 1, main start" << std::endl;
auto result = do_by_coroutine();
std::cout << " 9, resume co_await-suspend " << std::endl;
result.coro_.resume();
std::cout << "15, get yield_value: " << result.value() << std::endl;
std::cout << "16, resume co_yield-suspend " << std::endl;
result.coro_.resume();
std::cout << "21, main end" << std::endl;
return 0;
}
运行输出
1, main start
2, get_return_object
3, task construct
4, initial_suspend
5, before co_await
6, awaiter construct
7, await_ready
8, await_suspend
9, resume co_await-suspend
10, await_resume
11, awaiter deconstruct
12, after co_await
13, before co_yield
14, yield_value: 123
15, get yield_value: 123
16, resume co_yield-suspend
17, after co_yield
18, before co_return
19, return_value: 3
20, final_suspend
21, main end
22, task deconstruct
运行输出解读
第一块 1~4
调用协程函数do_by_coroutine,协程进入准备阶段。
先调用get_return_object构造协程特定返回对象,在函数get_return_object中构造并返回协程特定返回对象task。
紧接着调用initial_suspend初始化协程状态,文中为不挂起。
到此协程准备结束。
第二块 5~12
本块 属于最讲究的co_await操作符使用。
开始协程do_by_coroutine执行,先构造出awaiter对象,接着判断await_ready状态,文中是false,属于未ready状态,所以紧接着调用await_suspend,协程被挂起。
因为协程被挂起,所以从协程函数do_by_coroutine中跳出,往下执行以下语句:
std::cout << " 9, resume co_await-suspend " << std::endl;
result.coro_.resume();
然后协程被恢复,所以又跳回协程函数do_by_coroutine中,对应的await_resume函数被执行。
到此co_await awaiter{} 这一行 执行结束,awaiter对象被析构。打印出 “12, after co_await”。
第三块 13~17
本块 属于较为讲究的co_yield操作符使用。
目前还在协程函数do_by_coroutine中,所以接着执行co_yield “123” ,对应的yield_value函数被执行,同时yield_value形参from被设置为“123”。上篇文中提到co_yield是co_await的变体,且文中yield_value返回的是suspend_always,所以协程又一次被挂起。
因为协程被挂起,所以从协程函数do_by_coroutine中跳出,从第一句 result.coro_.resume() 处接着往下执行:
std::cout << "15, get yield_value: " << result.value() << std::endl;
std::cout << "16, resume co_yield-suspend " << std::endl;
result.coro_.resume();
然后协程被恢复,所以又跳回协程函数do_by_coroutine中,到此co_yield “123” 这一行 执行结束,打印出 “17, after co_yield”。
第四块 18~19
本块 属于最简单的co_return操作符使用。
目前还在协程函数do_by_coroutine中,所以co_return 3 被执行,对应的return_value函数被执行。协程函数执行结束。
第五块 20~22
因为第四块中的co_return操作符,所以协程执行结束。本块属于协程结束收尾阶段,final_suspend函数首先被调用。
main函数结束。
特定返回类型task的result对象被析构。
程序退出。
协程跳转
协程最让人难以理解的就是“跳来跳去”。其实理解起来也不难。
遇到co_return就表示协程结束,不会再有“跳来跳去”。
遇到co_yield或者co_await,需要看awaitable是 suspend_always还是suspend_never。不过协程通常会被挂起。
只要被挂起,就需要调用resume才能回到协程函数的“切出点”(调用co_yield或者co_await的那一行)。
只要被挂起,就会从当前协程函数中跳出,继续从"切入点"的下一行开始执行。
转载:https://blog.csdn.net/qq_15328161/article/details/128554203