协程
协程函数
若一个函数中出现了以下三个关键字之一,则编译器就知道这是一个协程函数
- co_await
- co_yield
- co_return
并且,协程函数必须返回一个符合要求的协程对象,若有返回值,需要通过协程对象获得
协程对象
协程函数的返回值是自定义的一个协程对象
模板
通常需要内嵌一个名为promise_type的结构定义,内部需要实现一些函数
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#include<iostream>
#include<coroutine>
struct MyCoroutine{
struct promise_type {
MyCoroutine get_return_object() {
return MyCoroutine();
}
std::suspend_never initial_suspend() { return {}; }
std::suspend_never final_suspend() noexcept { return {}; }
void return_void() {}
void unhandled_exception() {}
};
};
MyCoroutine coroFunc(){
std::cout <<"hello world" << std::endl;
co_return;
}
int main(){
MyCoroutine coro = coroFunc();
return 0;
}此时coroFunc刚开始执行就被挂起,MyCoroutine对象也没有实现任何协程,故后面的代码不会执行,稍微进行修改
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
#include<iostream>
#include<coroutine>
struct MyCoroutine{
struct promise_type {
MyCoroutine get_return_object() {
// 获得协程句柄
return MyCoroutine(std::coroutine_handle<promise_type>::from_promise(*this));
}
std::suspend_always initial_suspend() { return {}; }
std::suspend_always final_suspend() noexcept { return {}; }
void return_void() {}
void unhandled_exception() {}
};
// 通过构造函数传给新创建的对象
// 调用了协程句柄的resume函数
MyCoroutine(std::coroutine_handle<promise_type> h) : handle(h) {}
void resume() {
if (handle) {
handle.resume(); // 恢复协程执行
}
}
private:
std::coroutine_handle<promise_type> handle; // 封装了一个指向协程帧的指针,并提供函数
};
MyCoroutine coroFunc(){
std::cout <<"hello world" << std::endl;
co_return;
}
int main(){
MyCoroutine coro = coroFunc();
coro.resume(); // 恢复协程执行
return 0;
}返回值
若需要提供返回值,则需要将return_void改为return_value,并添加get函数存储并获取反返回值:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
#include<iostream>
#include<coroutine>
struct MyCoroutine{
struct promise_type {
MyCoroutine get_return_object() {
// 获得协程句柄
return MyCoroutine(std::coroutine_handle<promise_type>::from_promise(*this));
}
std::suspend_always initial_suspend() { return {}; }
std::suspend_always final_suspend() noexcept { return {}; }
void return_value(int v) {_value = v;}
void unhandled_exception() {}
public:
int get(){return _value;}
private:
int _value; // 存储协程返回值
};
// 通过构造函数传给新创建的对象
// 调用了协程句柄的resume函数
MyCoroutine(std::coroutine_handle<promise_type> h) : handle(h) {}
int get(){
handle.resume(); // 恢复协程执行
return handle.promise().get(); // 获取协程返回值
}
private:
std::coroutine_handle<promise_type> handle; // 封装了一个指向协程帧的指针,并提供函数
};
MyCoroutine coroFunc(){
std::cout <<"hello world" << std::endl;
co_return 666;
}
int main(){
MyCoroutine coro = coroFunc();
int value = coro.get();
std::cout << "value= " << value << std::endl;
return 0;
}co_yield
如果要使用co_yield,需要添加yield_value函数
由于co_yield不是函数执行的结尾,故需要返回一个可等待对象,并使用co_await对该对象进行等待,即
1
2
co_yield v;
co_await promise.yield_value(v);或者返回一个suspend_always对象,让程序挂起
1
auto yield_value(int v) { _value = v; return std::suspend_always{};}为方便使用,还可以重载一下布尔运算符,通过调用handle的done函数来判断协程是否执行完毕
1
operator bool(){return !handle.done();}在主函数中,就可以通过一个循环对协程执行进行挂起和继续操作,并获得每次的返回值
1
2
3
4
5
MyCoroutine coro = coroFunc();
while(coro){
int value = coro.get();
std::cout << "value= " << value << std::endl;
}应用:实现发生器(Generator)
一个斐波那契数列生成器
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
#include<iostream>
#include<coroutine>
struct MyCoroutine{
struct promise_type {
MyCoroutine get_return_object() {
// 获得协程句柄
return MyCoroutine(std::coroutine_handle<promise_type>::from_promise(*this));
}
std::suspend_always initial_suspend() { return {}; }
std::suspend_always final_suspend() noexcept { return {}; }
void return_value(int v) {_value = v;}
auto yield_value(int v) { _value = v; return std::suspend_always{};}
void unhandled_exception() {}
public:
int get(){return _value;}
private:
int _value; // 存储协程返回值
};
// 通过构造函数传给新创建的对象
// 调用了协程句柄的resume函数
MyCoroutine(std::coroutine_handle<promise_type> h) : handle(h) {}
operator bool(){return !handle.done();} // 重载bool运算符,判断协程是否完成
int get(){
handle.resume(); // 恢复协程执行
return handle.promise().get(); // 获取协程返回值
}
private:
std::coroutine_handle<promise_type> handle; // 封装了一个指向协程帧的指针,并提供函数
};
MyCoroutine fib(){
int a = 0, b = 1, n = 40;
co_yield a;
co_yield b;
while(n--){
int r = a+b;
co_yield r; // 每生成一项就把协程挂起,等待外部调用resume函数
a = b;
b = r;
}
co_return a+b;
}
int main(){
MyCoroutine coro = fib();
while(coro){
int value = coro.get();
std::cout << "value= " << value << std::endl;
}
return 0;
}co_await
语法:co_await 表达式
表达式可以是可等待对象,重载co_await()运算符,也可以是一个promise类型的await_transform()函数,将表达式作为函数参数并返回可等待对象
可等待类型awaitable的定义如下:
1
2
3
4
5
6
struct awaitable
{
bool await_ready();
void await_suspend(std::coroutine_handle<> h);
auto await_resume();
};调用对象时,首先调用await_ready(),若返回值为false,则调用await_suspend()让程序挂起,当协程继续执行后,会接下来执行await_resume(),若返回值为true,则直接执行await_resume()
内置类型suspend_always中await_ready()返回false,其余两个函数什么都不做,作用是将程序挂起
内置类型suspend_never中await_ready()返回true,其余两个函数什么都不做
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
#include<coroutine>
#include<iostream>
#include<thread>
struct promise;
struct MyCoroutine: std::coroutine_handle<promise>{
using promise_type = ::promise;
};
struct promise{
MyCoroutine get_return_object() {
return {MyCoroutine::from_promise(*this)};
}
auto initial_suspend() { return std::suspend_always{}; }
auto final_suspend() noexcept { return std::suspend_always{}; }
void unhandled_exception() {}
};
struct awaitable{
bool await_ready(){
std::cout << "await_ready called" << std::endl;
return false; // 返回false表示协程需要挂起
}
void await_suspend(std::coroutine_handle<> h){
std::cout << "await_suspend called" << std::endl;
// 在这里可以执行一些异步操作
// ...
h.resume(); // 恢复协程执行
}
auto await_resume() {
std::cout << "await_resume called" << std::endl;
return 666; // 返回协程的结果
}
};
MyCoroutine coroFunc(){
std::cout << "coroFunc started" << std::endl;
co_await awaitable{}; // 使用协程挂起
std::cout << "coroFunc resumed" << std::endl;
}
int main(){
MyCoroutine coro = coroFunc();
std::cout << "main started" << std::endl;
coro.resume(); // 恢复协程执行
std::cout << "main finished" << std::endl;
return 0;
}1
2
3
4
5
6
7
main started
coroFunc started
await_ready called
await_suspend called
await_resume called
coroFunc resumed
main finished根据返回结果,证明了函数调用的顺序是ready->suspend->resume