Download as pdf or txt
Download as pdf or txt
You are on page 1of 14

Coroutines 基本概念与极简模型​

官方Coroutines 资料​
Coroutines 参考 ​
https://en.cppreference.com/w/cpp/language/coroutines ​
Coroutines 标准 ​
http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/n4775.pdf ​
Coroutines 支持库 ​
https://github.com/lewissbaker/cppcoro ​

• task<T> ​
• shared_task<T> ​
• generator<T> ​
• async_generator<T> ​
• async_mutex<T> ​
Coroutines Hello world ​
https://blog.panicsoftware.com/your-first-coroutine/ ​
Coroutines 是什么​
使用者角度​
1. 同步方式​

2. 异步方式1​

3. 异步方式2​
4. boost::future

5. coroutines​
6. 代码实例​

抽象概念​
• 一种编译器支持的对 set_jump(...) long_jump(...)的安全封装, 实现线程栈间安全跳转​
• 堆上的栈
• 对boost::coroutines boost::coroutines2 boost::CO2 的标准化和取舍​
• goroutiens 的 c++版​
• 可挂起函数
• 建模: 2个动态过程(如线程)的交互合同订立基础标准​

从C语言角度看C++ coroutines​
三个重点​
• second stack frame​
• __builtin_coro_resume​
• __builtin_coro_suspend​
LLVM coroutins​
https://llvm.org/docs/Coroutines.html​
• C++​
• Rust​
• Webassembly

C++ Coroutines support builtins​


• void __builtin_coro_resume(void *addr);​
• void __builtin_coro_destroy(void *addr);​
• bool __builtin_coro_done(void *addr);​
• void *__builtin_coro_promise(void *addr, int alignment, bool from_promise)​
• char __builtin_coro_suspend(bool final);​

例子, <coroutine> CLANG 简单实现​


C++
1 template <> struct coroutine_handle<void>
2 {
3 void resume() const
4 {
5 __builtin_coro_resume(ptr);
6 }
7 void destroy() const
8 {
9 __builtin_coro_destroy(ptr);
10 }
11 bool done() const
12 {
13 return __builtin_coro_done(ptr);
14 }
15 // ...
16 protected:
17 void *ptr;
18 };
19 template <typename Promise> struct coroutine_handle : coroutine_handle<>
20 {
21 // ...
22 Promise &promise() const
23 {
24 return *reinterpret_cast<Promise *>(
25 __builtin_coro_promise(ptr, alignof(Promise), /*from-
promise=*/false));
26 }
27
28 static coroutine_handle from_promise(Promise &promise)
29 {
30 coroutine_handle p;
31 p.ptr = __builtin_coro_promise(&promise, alignof(Promise),
32 /*from-promise=*/true);
33 return p;
34 }
35 };
Other coroutine builtins​
• size_t __builtin_coro_size()​
• void *__builtin_coro_frame()​
• void *__builtin_coro_free(void *coro_frame)​
• void *__builtin_coro_id(int align, void *promise, void *fnaddr, void *parts)​
• bool __builtin_coro_alloc()​
• void *__builtin_coro_begin(void *memory)​
• void __builtin_coro_end(void *coro_frame, bool unwind)​
• bool __builtin_coro_param(void *original, void *copy)​

与普通函数的区别​
coroutines​ 普通函数​ 备注​
栈在 堆上​ 线程栈上​
哪里​
参数​ C++ C++ 大部分coroutines 参
数要传值​
1 std::future<> foo(bar_t 1 std::future<> foo(bar_t
2 { st& a)
3 co_return; 2 {
4 } 3 return;
4 }

lamb C++ 大部分coroutines


da​ lambda不能capture
variables
1 bar_t a;
2 foo_t b;
3
4 auto f = [=]()
5 {
6 //do something
7 return;
8 }
C++
1 bar_t a;
2 foo_t b;
3 auto f = [](bar_t a, fo
b)
4 ->std::future<>
5 {
6 //do something
7 co_return;
8 };

返回 C++ C++ 不能deduce返回类型​


值​
1 std::future<> foo() 1 auto foo()
2 { 2 {
3 co_return; 3 return std::future<>
4 } 4 }
5
6 // 下面在c++20 中非法​
7 // 这是stackful corouti
8 // 将来可能会实现??​
9 auto foo()
10 {
11 co_return;
12 }
13

分类​
stackless​ stackfull (假想代码) / Generic 备注​
Coroutines​
Lambd stackful
a​ coroutines 在lua
等脚本语言中支
持​
C++ C++ 假想代码在
c++20 中非法 ​
将来可能会实
1 auto fStackless = [](au
a)
1 auto fStackfull = [](auto
a)
现??​
2 -> generator<int> 2 {
3 { 3 co_yield a * a;
4 co_yield a * a; 4 }
5 } 5
6 6 std::future<> foo()
7 std::future<> foo() 7 {
8 { 8 auto yieldIt = fStackl
9 fStackless(1); ss(1);
10 co_return; 9 yieldIt();
11 } 10
11 generator<int> r = fSt
ckless(2);
12 co_return;
13 }

Functi C++ C++


on​
1 std::future<> foo() 1 auto foo()
2 { 2 {
3 co_return; 3 co_return;
4 } 4 }
5
6 //foo 是什么?​

• stackless​
• stackfull​
Coroutines 为什么​
解耦与建模:​
◦ 编译器级将动态过程封装为对象, 并提供和外界交互的基础规则​
◦ 很大程度上提高了函数式编程的适用性,现在函数可完成动态模型转换
◦ coroutines模式创建(库撰写)与库使用者分离良好​
◦ 提供了一种可能: 将并发同步问题限制在库(模式)的开发中, 减少库(模式)使用者对并发同步的需

性能: ​
◦ 充分利用编译器对local scope的优化能力​
◦ 标准建议编译器尽可能减少堆上栈的使用
◦ 大量减少对线程的需要
◦ 大量减少线程上下文切换的需要
高并发: ​
◦ 编译器级(LLVM built-in) 上下文切换; ​
◦ 大幅度减少锁的使用; ​
◦ 为尽可能用atomic替代锁提供基础​
易用性: ​
◦ coroutines的使用者逻辑符合常规同步思维​
◦ 减少传递不必要的参数或者减少全局变量
◦ 已经足够成熟 Microsoft 2019年推出 WindowsRT库大量使用, 取代MFC, C++/CRT​
◦ 异步数据可分段 co_yield (future 不能)​
Coroutines How​
Coroutines 编译器及库支持​

Coroutines 代码生成及调试​
gdb/lldb 最新版暂时还不支持 读取coroutins 栈中的变量​
常规线程栈
极简模型​
C++ C++
1 // 前台代码, 库的使用者写​ 1 // 后台库代码2 ​
2 T foo() 2 class P
3 { 3 {
4 //...... 4 waitable_t yield_value(auto&&)
5 w_t w; 5 {
6 float a = co_await w; 6 //......
7 //...... 7 }
8 co_yield 20.5; 8 void return_value(auto&&)
9 //...... 9 {
10 co_return 100; 10 //......
11 } 11 }
12 12 };
13

C++
1 // 后台库代码3​
2 // 侵入式​
3 struct T
4 {
5 using promise_type = P;
6 };
7
8 // 非侵入式 需 partial specify
std::coroutine_traits
9 template<>
10 struct std::coroutine_traits<T>
11 {
12 using promise_type = P;
13 };
C++
1 // 后台库代码1​
2 // waitable_t
3 struct w_t
4 {
5 bool await_ready()
6 {
7 return false;
8 }
9
10 void await_suspend(auto
coro)
11 {
12
13
__builtin_coro_resume(coro)
;
14 }
15
16 float await_resume()
17 {
18 return 0.9;
19 }
20 };
21

与ECMAScript6概念对比​
js 异步 ​ C++协程​ 备注​
await​ co_await​ 关键字​
yield​ co_yield​ 关键字​
Promise​ cppcoro::task<>​ 库​
Generator​ cppcoro::generaor<>​ 库​
AsyncGenerator​ cppcoro::async_generaor<>​ 库​
Promise.all()​ cppcoro::when_all(...)​ 库​
Promise.any()​ when_any(...)​ 自己写的​
Promise.prototype.then()​ f_then(...)​ 自己写的​
Promise.resolve()​ f_make_ready_task(...)​ 自己写的​
Promise.reject()​ f_make_exceptional_task(...)​ 自己写的​
Promise.race()​ f_xxx()​ 研究中​
JavaScript C++ 自己写的​

1 async (()=>{ 1 []() -> co_run_it_t {


2 //await ... 2 //co_await ...
3 //do_sth 3 //do_sth
4 })(); 4 }();

...​ ...​ ...​

相同点​
• 等待语义
• yield语义​
• 解决callback陷阱​
• when_all(...)​
• when_any(...)​
• 都是stackless的​
不同点​
透明度​ 线程​ 模式​ 同步等待​ 易用性​ Performan
ce​
C++协程​ 完全透明​ 多线程​ 高度可定制​ cppcoro::sync_ 中​ 高​
背后是sjlj​ 有时仍需要mutex等 cppcoro 里用 wait(...)​ std::ta 很多并发同
同步操作​ coroutine甚至实 sk<>​ 步可用
现了协作式 pendin atomic模
mutex, latch, g​ 型完成​
fence 及锁 ​
js 异步 ​ 不透明​ 没有线程概念​ ECMAScript标准 无​ 高​ 高​
V8实现可能使用了并 规定​ 取决于v8
发??​ • Promise​ 实现​
• Generator​
• AnsycGenerat
or​
• AsyncIterable​

You might also like