C++协程基础

参考资料

一、什么是协程

1.1 基础知识

通用的说法是协程是⼀种“轻量级线程”,用户态线程”。

  • 可以减少用户态与内核态的切换,相当于一个花里胡哨的函数。

协程的本质就是函数和函数运行状态的组合 。

  • 函数一旦被调用,则只能从头运行到结束
  • 协程可以执行到一半退出(yield),暂时让出CPU执行权;在后面适当时机可以重新恢复运行(resume)。这期间CPU可以运行其他协程。

协程能够半路yield、再重新resume的关键是协程存储了函数在yield时间点的执⾏状态,这个状态称为协程上下文

  • 通过记录当前执行状态下CPU全部寄存器的值来实现
  • 要resume时再把这些值重新设置给CPU

与线程的区别:

  • 线程创建之后,线程的运⾏和调度也是由操作系统⾃动完成的
  • 协程创建后,协程的运⾏和调度都要由应用程序来完成,就和调用函数⼀样,所以协程也被称为“用户态线程”。

1.2 优缺点

优点:

  • 提高资源利用率,提高程序并发性能。协程允许开发者编写异步代码,实现非阻塞的并发操作,通过在适当的时候挂起和恢复协程,可以有效地管理多个任务的执行,提高程序的并发性能。与线程相比,协程是轻量级的,它们的创建和上下文切换开销较小,可以同时执行大量的协程,而不会导致系统负载过重,可以在单线程下实现异步,使程序不存在阻塞阶段,充分利用cpu资源。
  • 简化异步编程逻辑。使用协程可以简化并发编程的复杂性,通过使用适当的协程库或语言特性,可以避免显式的线程同步、锁和互斥量等并发编程的常见问题,用同步的思想就可以编写成异步的程序。

缺点:

  • 无法利用多核资源。线程才是系统调度的基本单位,单线程下的多协程本质上还是串行执行的,只能用到单核计算资源,所以协程往往要与多线程、多进程一起使用。

二、协程的分类

2.1 对称协程与非对称协程

  • 对称协程,协程可以不受限制地将控制权交给任何其他协程。任何⼀个协程都是相互独⽴且平等的,调度权可以在任意协程之间转移。
  • ⾮对称协程,是指协程之间存在类似堆栈的调⽤⽅-被调⽤⽅关系。协程出让调度权的⽬标只能是它的调⽤者。

对称协程更灵活,⾮对称协程实现更简单。

2.2 有栈协程与无栈协程

浅谈有栈协程与无栈协程
有栈协程:用独立的执行栈来保存协程的上下文信息。

  • 当协程被挂起时,栈协程会保存当前执行状态(例如函数调用栈、局部变量等),并将控制权交还给调度器。
  • 当协程被恢复时,栈协程会将之前保存的执行状态恢复,从上次挂起的地方继续执行。

无栈协程:它不需要独立的执行栈来保存协程的上下文信息,协程的上下文都放到公共内存中,

  • 当协程被挂起时,无栈协程会将协程的状态保存在堆上的数据结构中,并将控制权交还给调度器
  • 当协程被恢复时,无栈协程会将之前保存的状态从堆中取出,并从上次挂起的地方继续执行。
  • 协程切换时,使用状态机来切换,就不用切换对应的上下文了,因为都在堆里。

2.3 独立栈与共享栈

独立栈和共享栈都是有栈协程。

共享栈本质就是所有的协程在运行的时候都使用同一个栈空间,每次协程切换时要把自身用的共享栈空间拷贝。

  • 好处是使用公共资源,公共资源内存空间比较大,相对安全,节省内存空间,
  • 坏处是协程频切换需要进行内存拷贝,废CPU

独立栈,也就是每个协程的栈空间都是独立的,固定大小。

  • 好处是协程切换的时候,内存不用拷贝来拷贝去,相对简单
  • 坏处则是内存空间浪费,容易栈溢出。

三、C++的协程

参考资料2。

在 C++ 当中,区别一个函数是不是协程,是通过它的返回值类型来判断的。一个函数的返回值类型如果是符合协程的规则的类型,那么这个函数就是一个协程。

C++ 协程会在开始执行时的第一步就使用 operator new 来开辟一块内存来存放这些信息,这块内存或者说这个对象又被称为协程的状态(coroutine state)。

关键字co_await ,使当前函数(协程)的执行被挂起。其操作对象为等待体(awaiter)。

等待体一般需要实现三个函数,用于挂起和恢复时使用。

  • await_ready:协程挂起
  • await_suspend:用于保存局部变量和挂起点,最为核心的函数之一了
  • await_resume:挂起恢复

四、实现一个简单的协程库

ucontext-人人都可以实现的简单协程库

<think>好的,我现在需要回答用户关于C++协程的教程和示例的问题。首先,我需要确认用户的需求,他们可能希望了解C++协程的基本概念、使用方法以及实际例子。接下来,我应该回忆C++协程的相关知识,特别是C++20引入的协程特性。 首先,协程是什么?协程是一种允许函数暂停和恢复执行的机制,适用于异步编程和生成器模式。C++20引入了协程的标准支持,但实现可能因编译器而异,需要确认用户的开发环境是否支持,比如MSVC、GCC或Clang的版本。 然后,我需要解释协程的关键组件。根据C++20标准,协程涉及co_await、co_yield和co_return关键字。协程框架包括promise_type、coroutine_handle等。这部分需要详细说明每个组件的作用,比如promise_type负责协程的状态管理,coroutine_handle用于控制协程的生命周期。 接下来,示例代码是必要的。用户可能需要一个简单的生成器示例,比如生成斐波那契数列,或者一个异步任务的示例。例如,使用协程实现一个简单的生成器,每次调用生成下一个值。同时,要确保代码正确,符合C++20标准,并且注意编译器可能需要特定的标志,比如GCC的-fcoroutines。 另外,需要提到协程的优缺点,比如简化异步代码的结构,但可能存在编译器支持不完善或性能开销的问题。还要注意协程的使用场景,比如网络编程、游戏中的状态机等。 用户提供的引用中提到了C++与Lua的交互、Android Studio配置和CMake教程,但当前问题集中在协程,因此可能不需要直接引用这些内容。但如果有相关点,比如在C++模块中结合协程和Lua脚本,可以适当提及,但需要确认是否有必要。 最后,生成相关问题,帮助用户深入理解,比如协程与线程的区别、性能优化、在异步I/O中的应用等。确保问题覆盖常见的使用场景和潜在问题。 总结回答结构:简介协程概念,C++20的支持,关键组件和关键字,示例代码(生成器和异步任务),优缺点,应用场景,相关问题。确保使用正确的LaTeX格式,如$co_await$,并引用相关资源。</think>### C++协程教程与示例 #### 1. 协程基础 C++20正式引入协程(Coroutine)作为标准特性,它允许函数在执行过程中暂停并恢复,适用于异步编程、生成器(Generator)等场景。协程通过$co\_await$、$co\_yield$和$co\_return$关键字实现控制流的挂起与恢复[^1]。 #### 2. 协程核心组件 - **协程框架**:由编译器生成的代码结构,管理协程状态。 - **Promise类型**:定义协程的行为(如返回值、异常处理),需实现`get_return_object()`、`initial_suspend()`等方法。 - **协程句柄(Coroutine Handle)**:通过`coroutine_handle<Promise>`操作协程生命周期。 - **Awaitable对象**:定义`await_ready()`、`await_suspend()`和`await_resume()`,控制挂起逻辑。 #### 3. 示例:生成器(Generator) 以下代码演示一个生成斐波那契数列的协程: ```cpp #include <coroutine> #include <iostream> // 定义Generator类 template<typename T> struct Generator { struct Promise { T current_value; auto yield_value(T value) { current_value = value; return std::suspend_always{}; } auto initial_suspend() { return std::suspend_always{}; } auto final_suspend() noexcept { return std::suspend_always{}; } Generator get_return_object() { return Generator{this}; } void unhandled_exception() {} }; using promise_type = Promise; std::coroutine_handle<Promise> coro; explicit Generator(Promise* p) : coro(std::coroutine_handle<Promise>::from_promise(*p)) {} ~Generator() { if (coro) coro.destroy(); } bool next() { if (!coro.done()) { coro.resume(); return !coro.done(); } return false; } T value() { return coro.promise().current_value; } }; // 使用协程生成斐波那契数列 Generator<int> fibonacci(int max) { int a = 0, b = 1; while (a <= max) { co_yield a; auto tmp = a; a = b; b += tmp; } } int main() { auto gen = fibonacci(100); while (gen.next()) { std::cout << gen.value() << " "; } // 输出: 0 1 1 2 3 5 8 13 21 34 55 89 } ``` #### 4. 协程的优缺点 - **优点**:简化异步代码逻辑,减少回调地狱;轻量级上下文切换,资源占用低于线程。 - **缺点**:编译器支持需C++20及以上(如GCC 10+、MSVC 2019+);调试复杂性较高。 #### 5. 应用场景 - **异步I/O**:网络请求、文件读写(结合I/O多路复用)。 - **游戏开发**:状态机、动画序列控制。 - **数据处理**:流式数据生成(如日志解析)。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值