30 | Coroutines:协作式的交叉调度执行
该思维导图由 AI 生成,仅供参考
什么是协程?
- 深入了解
- 翻译
- 解释
- 总结
C++20引入了协程(Coroutines)这一重要的新功能,允许函数在执行过程中被挂起和恢复,为协作式多任务提供了支持。本文深入浅出地介绍了协程的概念和在C++20中的应用,对比展示了在C++和Python中实现斐波那契数列生成器的代码。文章详细讨论了C++20中协程的基础,包括协程的常见用途和新关键字,如`co_await`、`co_yield`和`co_return`。作者还展示了如何使用协程实现斐波那契数列生成器,并给出了调用代码的示例。此外,介绍了C++20协程的高层抽象,如cppcoro库提供的`generator`和MSVC的私有扩展。文章还讨论了有栈协程和无栈协程的区别,以及编译器对协程的支持。总的来说,本文全面介绍了协程的概念和在C++20中的应用,为读者提供了深入的了解和实践指导。
《现代 C++ 编程实战》,新⼈⾸单¥59
全部留言(11)
- 最新
- 精选
- 吴咏炜置顶参考资料 [3] 的 cppcoro 目前看起来已经不再维护了,跟新版编译器的兼容性问题一直无人解决。目前比较好的复刻在下面这个链接: https://github.com/andreasbuhr/cppcoro2022-03-102
- 易轻尘个人对协程的理解,可能不太准确: 不可以混淆线程和协程两个概念。计算机看到的是线程,调度的也是线程。一个线程中可以有很多个协程,这些协程的执行顺序由程序员自己来调度。比较明显的好处是,1. 同一个线程中的协程不需要考虑数据的竞争问题,因为这些协程的执行顺序是固定的;2. 协程能够很方便的保存执行状态,使复杂状态机的实现变得简单;
作者回复: 「计算机看到的是线程,调度的也是线程。」 把「计算机」改成「操作系统」,你的评论就没问题了。
2020-06-2837 - Fireplusplus无栈协程的内存布局可以明白函数D调用结束后,应该是通过编译器处理后的方式返回到协程C的堆上空间,协程c挂起之后堆仍然是保留的,但是一个有栈协程的内存布局应该是什么样子的,协程挂起之后不是要出栈才能回到调用者的栈桢吗?
作者回复: 无栈协程在进入协程时仍会使用原先的栈帧,所有的本地变量则固定分配在堆上的一块内存里。有栈协程在执行时会启用一个自己的独立栈帧,后面挂起和恢复执行时也都需要切换栈帧,因而开销比无栈的要大。 对具体细节感兴趣的话,网上这篇文章说得还比较细: https://mthli.xyz/stackful-stackless/
2021-05-153 - 谦谦君子老师, 图左边“调用X的参数”在“返回Y的地址”下面, 而右边“调用X的参数”在“返回Y的地址”上面, 是画错了么, 还是协成里面就是跟栈上函数调用是反的呢?
作者回复: 在栈里是一个明确的压栈顺序的(x86或类似平台上不管什么编译器实现都差不多)。而协程的数据放在堆里,并没有类似的顺序惯例,实际实现的顺序可能完全不一样。
2020-02-122 - 李云龙老师下面的这个函数体内没有写co_return,为什么函数的返回值就可以写uint64_resumable ? uint64_resumable fibonacci() { uint64_t a = 0; uint64_t b = 1; while (true) { co_yield b; auto tmp = a; a = b; b += tmp; } }
作者回复: 有co_的其中之一就是协程了。协程的第一次“返回”不是返回语句。
2023-11-06归属地:北京1 - englefly吴老师,请教两个问题: 1。coroutine的能力是不是比线程弱?我们可以用线程的 mutex+condition-variable 或 primise+future 模拟coroutine。但coroutine究竟哪些方面弱于线程呢? 2。coroutine的底层实现究竟是什么?它有cpu的上下文切换吗?还是所有coroutine都运行在同一个线程里,coroutine只是fibonacci c++实现那样的一种代码组织形式? 谢谢
作者回复: 最基本协程和线程的区别就是,协程是程序自己调度的,线程是操作系统调度的。操作系统干的事情,开销自然大一些。反过来,协程的优势,性能就是很重要的一块了。 协程在哪个线程运行,是你的代码决定的。你从哪个线程去调用协程或去 co_await/co_yield 了,协程就在那个线程执行。像生成器那样的用法,完全只有一个线程,也不适合用多线程来改造。
2020-03-291 - Gallen您好,吴老师,之前在2018年cpp开发者大会上听过您讲string view和range,还巧妙的使用|管道符进行函数间对象传递,能否有幸添加一下您的微信?谢谢
作者回复: 有事情先发我邮件好了。应该不会找不到吧。
2020-02-2021 - 晚风·和煦老师,map<int, int>().swap(map1); 这个语句为什么不能达到真正释放map1内存的效果呢?必须得用malloc_trim
作者回复: 先说是不是,然后才谈得上为什么。 在大部分情况下都没有必要那么做。我从来没在代码里写过 malloc_trim。 在有虚拟内存的世界里,我看不出调用 malloc_trim 的必要性。操作系统自己能管好。 如果嵌入式开发,没有虚拟内存,你用 malloc_trim 也不见得有用。因为一旦堆的尾部有分配,你并不能释放内存回操作系统。 而且,你为什么要还内存给操作系统?以后你不用了吗?是程序要退出了吗?如果程序要退出,本来占用的资源就会被释放掉。如果程序不退出,进程管好自己的事,下次分配能不麻烦操作系统就不麻烦操作系统,应该反而更好。 我的个人见解,谁告诉你这句话的,基本上并不真懂应用开发,只是学了点 Linux 的知识,在瞎卖弄而已。
2020-02-111 - Vackine感觉跟python里面的async的新的标准库好像,是真的有性能上的提升么,还是只是编程魔法?
作者回复: 编程魔法和性能提升有矛盾么?🤪 严肃点,协程不是为了提高代码性能,而是为了提高程序员的生产率。从这点上来说,协程仍然是一种编译器的黑魔法。 跟手写比起来,没有性能的提升。就如同除了极少数的情况(比如泛型允许内联导致C++的排序比C的qsort快),C++代码不会比C代码性能更高。
2020-02-101 - 徐很早了解过协程的概念,说是用户级线程,在用户态调度,避免线程的内核态切换,性能更高; 后来在一个 C++ 服务框架上也使用过,再后来切到 Go,用起了 goroutine; 简单来说,协程用起来是真爽,避免了恶心的异步 IO 的 callback 写法,自我感觉也算是理解并熟练使用协程了。 直到看了 C++ 20 的协程,完了惊掉下巴玩脑袋问号,这什么玩意儿,这是协程吗? 查各种资料和书,都跟以前理解的不一样,特别是看到有人所谓的用几行代码实现协程,那更是不知所以。 最后终于在这里解开了我的疑惑,原来以前理解和使用的协程,和 C++ 20 里的协程,一个是有栈协程,一个是无栈协程,有巨大差异,为啥在那些讲现代 C++ 的书里,都不提这个点,这个很关键呐,真是困扰我很久。
作者回复: 现在著名的异步框架 Asio 也支持协程了。阿里也有个协程库 PhotonLib。小而美的可以研究一下 netcan/asyncio。
2023-02-24归属地:广东