公司一些老项目使用了 brpc,写全异步的时候被回调地狱折磨,一想到现在都快 c++23 了,何不用协程解决问题,而现有的开源协程框架都要求从底层用起(很难与 brpc 结合)
看完c++20 协程文档(感觉每一句都挺重要。。),有了用引用计数方案管理协程的想法
简单的说,就是当一个协程被 co_await 挂起后,由最晚运行的回调线程负责恢复,这样就不用从底层开始管理协程生命周期了
所有包装使用的 异步函数 必须满足
void func(int a, std::function<void(int)) cb)
https://github.com/kkHAIKE/sco/blob/main/main.cpp
解释:
some(1, 2).start_root_in_this_thread();
在一个线程中开始启动协程co_await sco::call_with_callback(&test, a, b, sco::cb<void(int,int)>(c, d));
包装 异步函数 test输出
test return
some end
2,3
test return
some return
3,4
1
ysc3839 2023-03-28 17:18:07 +08:00 via Android
你这个库引入了额外的线程,能兼容现有的库吗?我自己的方案是仿照 JavaScript 的 Promise
https://gist.github.com/ysc3839/91dfa5b4f80caa05ea6d062476bbb66c |
3
kkhaike OP 上面文案有误 '在一个线程中开始启动协程' -> '在当前线程中开始启动协程',才会造成上面的误解
|
5
leonshaw 2023-03-28 18:59:25 +08:00
这样只能把本来回调里的逻辑移到外面,但是有的场景回调参数生命周期只在 func 内部(这应该是无栈协程的硬伤)。 另外协程 resume 在异步函数内部的线程,如果是个第三方库提供的,可能影响它的线程管理。
|
6
kkhaike OP @leonshaw
1. 周期在内部的,可以考虑 move 出来,很少有不能 move 的吧。。 2. 方向反了。。是异步线程 resume 协程,不会影响线程管理,实际上整个执行流程和你写回调地狱是一致的。。 感觉这套逻辑比较抽象,有点难解释,但是能够最大限度保证无依赖无损接入协程 |
7
leonshaw 2023-03-28 19:52:02 +08:00
@kkhaike
1. 考虑一个 visitor 函数,实现是持有锁的时候调用回调,然后释放锁,回调参数是某种 iterator 。对这个 iterator 的 move/copy 没有意义,因为一旦释放锁,访问就不是安全的。 2. 没反,比如一个库内部有一个线程池,协程 resume 以后会阻塞这个线程。你可能认为本来回调就是运行在异步线程上的,但是两个 co_await 之间并不是只有原来回调的逻辑。 |
8
ysc3839 2023-03-28 19:54:46 +08:00 via Android
@leonshaw 协程 resume 就等价于调用回调函数,协程会阻塞这个线程的话,回调函数也会,并没有问题
|
9
jdz 2023-03-28 19:57:37 +08:00 via Android
存量库还是不能用吧,比如各种 client ,redis++ client mysql client blabla
|
10
leonshaw 2023-03-28 20:02:41 +08:00
@ysc3839
看 #7 ,协程并不只有回调,例如 op 的例子加几行: co_await sco::call_with_callback(&test, a, b, sco::cb<void(int,int)>(c, d)); std::cout << c << ',' << d << std::endl; std::cin >> a >> b; co_await sco::call_with_callback(&test, a, b, sco::cb<void(int,int)>(c, d)); std::cout << c << ',' << d << std::endl; 正常情况下回调只有 cout ,但是这里 cin 也是阻塞在同一个线程的 |
12
kkhaike OP @leonshaw 没大懂你的点。。如果那个地方写了 cin 那么 相当于是 把 cin 写在回调里面,同样也是阻塞了回调函数啊
|
14
kkhaike OP 另外上面说不能 move 和 copy 的情况可以不走 co_await ,直接按普通函数调用也不会阻碍流程
|
15
jdz 2023-03-28 21:01:44 +08:00
@kkhaike 异步客户端一般都是在自己的线程中调用异步回调, 比如异步 redis 客户端, 那么在 callback 中唤醒挂起的协程?
|
19
jdz 2023-03-28 21:48:52 +08:00 via Android
@kkhaike 我还是有个疑问,比如 redis client ,callback 是在 redis client 库自己起的线程中执行的,库自己的线程怎么知道执行完 callback 后去唤醒协程呢,我猜是编译器在 callback 代码块的后面加了唤醒的代码?
|
20
jdz 2023-03-28 21:49:39 +08:00 via Android
@kkhaike 我还是有个疑问,比如 redis client ,callback 是在 redis client 库自己起的线程中执行的,库自己的线程怎么知道执行完 callback 后去唤醒协程呢,我猜是编译器在 callback 代码块的后面加了唤醒的代码? 这么说的话 callback 也要符合文档规范?
|
21
kkhaike OP @jdz 是我加的,协程接口主要是实现 co_await 这个关键字的细节,我在给异步函数的回调中加入了恢复协程的代码
|
22
kkhaike OP 该项目已经 release 了第一个版本,让老项目使用协程 happy 起来吧 😊
|
23
weeei 2023-04-21 16:28:14 +08:00
good ,mark
|