由于搜索出来的帖子,都是老版本的实验协程,很多老的代码已经失去参考性,并且很复杂,所以就自己研究了一下。
1 #include <iostream> 2 #include <coroutine> 3 #include <thread> 4 5 template<typename _Ty> 6 struct cocontext { 7 struct promise_type; 8 using _Hty = std::coroutine_handle<promise_type>; 9 struct promise_type { 10 // 只要一个函数的返回值是 cocontext<T>,这个函数内存在co_await co_yield co_return这3个语法糖 11 // 就会第一时间调用 get_return_object 12 cocontext get_return_object() { 13 return { _Hty::from_promise(*this) }; 14 } 15 16 // 协程异常时抛出到这里,关于异常,见仁见智,反正我是不用的。 17 void unhandled_exception() { std::terminate(); } 18 19 // co_await co_yield co_return 之前回调,只回调一次,它在get_return_object之后回调 20 auto initial_suspend() { return std::suspend_never{}; } 21 22 // 协程函数返回之后,回调这里 23 auto final_suspend() { return std::suspend_always{}; } 24 25 // 如果函数里没有co_return,必须实现return_void,与之相对的还有一个return_value,类似yield_value 26 void return_void() {} 27 28 // 如果函数里有co_yield,必须实现yield_value,这其实并不难理解,co_yield把数据传到参数,然后我储存在_Val里而已 29 auto yield_value(const _Ty &val) { 30 _Val = val; 31 return std::suspend_always{}; 32 } 33 _Ty _Val; 34 }; 35 36 // 如果 await_ready 返回true,它就会继续执行,所以理论上来说,要模拟异步场景,都只会是return false 37 bool await_ready() const{ return false; } 38 39 // await_suspend是可以有参数的,它还可以是 void await_suspend(std::coroutine_handle<cocontext> handle); 40 // 在co_await co_yield co_return时,首先会调用这里,也就是可以根据情况直接在这里进行handle.resume(); 41 void await_suspend() {} 42 43 // 下面是我自己的实现 44 cocontext& resume() { if (!_Handle.done())_Handle.resume(); return *this; } 45 operator _Ty() const { return _Handle.promise()._Val; } 46 47 _Hty _Handle; 48 }; 49 50 int main() 51 { 52 auto test = []()->cocontext<int> { for (int i = 0; i < 10; i++) co_yield i; }; 53 auto c = test(); 54 std::cout << (int)c << std::endl; // 0 55 std::cout << (int)c.resume() << std::endl; // 1 56 std::cout << (int)c.resume() << std::endl; // 2 57 std::cout << (int)c.resume() << std::endl; // 3 58 std::cout << (int)c.resume() << std::endl; // 4 59 60 std::thread([&]() { 61 // 协程真正有意思的地方是,它可以由不同的线程去resume,这会很有意义。 62 std::cout << (int)c.resume() << std::endl; // 5 63 std::cout << (int)c.resume() << std::endl; // 6 64 std::cout << (int)c.resume() << std::endl; // 7 65 std::cout << (int)c.resume() << std::endl; // 8 66 std::cout << (int)c.resume() << std::endl; // 9 67 68 // 这里依旧输出9,上面一个c.resume()之后,test函数已经跳出循环返回了,已经满足了_Handle.done(),不会再继续真正的_Handle.resume(); 69 std::cout << (int)c.resume() << std::endl; 70 }).join(); 71 72 } 73 74 /* 75 后话: 76 C++ 20所谓的协程,实际上在我看来,更像是语法糖内部包含了一堆回调函数 77 并且这些回调函数得让程序员自己去完全实现,这确实是C++的风格, 78 但说实在的,我相信搞得清楚这些东西的人,都不会太喜欢这种风格。 79 80 虽然我的例子中 cocontext是基本可重用的类型,但我依旧没感觉这种形式为我的程序带来了多少便利。 81 情况还是那个情况,这个东西用起来并不方便,程序员从一开始就要考虑到所有细节, 82 可能最终很多细节考虑之后,用起来还是一大堆代码,到时候可能写着写着把递归逻辑搞成线性逻辑了,然后感叹一句,“我是SB,我TM用个P的协程!”。 83 */