当前位置: 首页 > news >正文

C++ 协程

C++20 引入了协程(Coroutines),协程是一种可以在执行过程中暂停和恢复的函数,允许在特定点挂起(suspend)执行,并将控制权交还给调用者,稍后可以从暂停点恢复执行。

应用场景

与传统的函数调用不同,协程可以在不丢失上下文的情况下暂停,适合以下场景:

1.异步编程:例如处理 I/O 操作(如网络请求、文件读写)时,避免阻塞线程。

2.生成器:逐步生成数据序列,适合处理大数据流或惰性求值。

3.协作式多任务:多个任务协作运行,共享线程资源。

关键字

C++ 的协程是通过编译器支持的底层机制实现的,主要依赖三个关键字:

1.co_await:暂停协程并等待某个异步操作完成。

2.co_yield:暂停协程并向调用者返回一个值(用于生成器)。

3.co_return:结束协程并返回结果。

关键组件

C++ 协程的实现依赖于几个关键组件,这些组件共同定义了协程的行为:

1.Promise 对象(promise_type):每个协程都有一个关联的 promise_type,它定义了协程的行为和返回值。promise_type 是一个类,通常包含以下方法:

get_return_object():定义协程的返回值对象(通常是一个协程句柄或自定义类型)。

initial_suspend():决定协程在启动时是否立即挂起(返回 std::suspend_alwaysstd::suspend_never)。

final_suspend():决定协程在结束时是否挂起。

return_void()return_value(T):处理协程的返回值。

yield_value(T):处理通过 co_yield 返回的值(生成器场景)。

unhandled_exception():处理协程中未捕获的异常。

2.协程句柄(std::coroutine_handle):std::coroutine_handle 是一个模板类,用于管理协程的状态和生命周期。它可以:恢复协程(resume()),检查协程是否完成(done()),销毁协程(destroy())。

3.协程框架:协程的执行需要一个框架来管理暂停和恢复。C++20 标准库没有提供内置的协程调度器,因此开发者通常需要:手动管理协程句柄,使用第三方库(如 liburingboost::asio)提供的事件循环或调度器。

4.暂停点:暂停点由 co_awaitco_yield 定义:

co_await expr:暂停协程,等待表达式 expr(通常是一个等待器,awaitable)完成。

co_yield expr:暂停协程并向调用者返回一个值。

暂停时,协程的状态(包括局部变量)被保存在堆上,称为协程帧(coroutine frame)。

工作原理

C++ 协程的实现依赖编译器将协程函数转换为状态机(state machine)。其工作原理如下:

1.函数转换:当编译器遇到 co_awaitco_yieldco_return 时,它将协程函数拆分为多个状态,每个状态对应一个暂停点或结束点。

2.协程帧分配:协程的局部变量和状态存储在堆上的协程帧中,确保暂停后状态不丢失。

3.状态机执行:协程在暂停点挂起时,返回控制权给调用者;恢复时,从暂停点继续执行。

4.生命周期管理:协程帧的分配和销毁由 promise_typestd::coroutine_handle 管理。

示例

生成器示例(使用 co_yield

这个示例实现一个生成整数序列的协程:

#include <iostream>
#include <coroutine>
#include <optional>// 生成器类型
template<typename T>
struct Generator {struct promise_type {std::optional<T> value;Generator get_return_object() {return Generator{std::coroutine_handle<promise_type>::from_promise(*this)};}std::suspend_always initial_suspend() { return {}; }std::suspend_always final_suspend() noexcept { return {}; }void return_void() {}std::suspend_always yield_value(T val) {value = val;return {};}void unhandled_exception() {}};struct Iterator {std::coroutine_handle<promise_type> coro;bool done;Iterator& operator++() {if (!coro.done()) {coro.resume();done = coro.done();}return *this;}T operator*() const {return *coro.promise().value;}bool operator!=(const Iterator& other) const {return !done;}};std::coroutine_handle<promise_type> coro;Generator(std::coroutine_handle<promise_type> h) : coro(h) {}~Generator() { if (coro) coro.destroy(); }Iterator begin() {coro.resume(); // 运行到第一个 yieldreturn {coro, coro.done()};}Iterator end() {return {coro, true};}
};// 生成 1 到 n 的序列
Generator<int> generate_sequence(int n) {for (int i = 1; i <= n; ++i) {co_yield i;}
}int main() {for (int val : generate_sequence(5)) {std::cout << val << " "; // 输出: 1 2 3 4 5}std::cout << "\n";return 0;
}

说明:Generator 是一个自定义协程类型,存储通过 co_yield 返回的值。promise_typeyield_value 方法将每次 co_yield 的值保存到 values 向量中。main 函数调用生成器并打印结果。

异步示例(使用 co_await

以下是一个模拟异步任务的协程,假设有一个简单的等待器:

#include <iostream>
#include <coroutine>
#include <chrono>
#include <thread>// 模拟的等待器
struct Awaiter {bool await_ready() { return false; } // 总是暂停void await_suspend(std::coroutine_handle<> h) {// 模拟异步操作(如 I/O)std::thread([h]() {std::this_thread::sleep_for(std::chrono::milliseconds(100));h.resume(); // 恢复协程}).detach();}void await_resume() {}
};// 异步任务
struct Task {struct promise_type {Task get_return_object() { return {}; }std::suspend_never initial_suspend() { return {}; }std::suspend_always final_suspend() noexcept { return {}; }void return_void() {}void unhandled_exception() {}};
};Task async_task(int id) {std::cout << "Task " << id << " started\n";co_await Awaiter{}; // 模拟异步等待std::cout << "Task " << id << " completed\n";
}int main() {// 启动 5 个异步任务(避免 10000 个以简化输出)std::vector<Task> tasks;for (int i = 0; i < 5; ++i) {tasks.push_back(async_task(i));}// 等待所有任务完成(简单模拟)std::this_thread::sleep_for(std::chrono::milliseconds(200));return 0;
}

说明:Awaiter 模拟一个异步操作(如网络 I/O),在 100ms 后恢复协程。co_await Awaiter{} 暂停协程,直到异步操作完成。main 启动多个异步任务,实际场景中需要事件循环来管理。

协程的优缺点

优点

1.高效性:协程在用户态管理暂停和恢复,避免了线程切换的开销。

2.灵活性:C++ 协程是底层机制,允许开发者自定义调度和行为。

3.异步编程简化:与回调或 std::future 相比,协程代码更直观,类似同步代码。

4.生成器支持:适合处理流式数据或惰性求值。

缺点

1.复杂性:需要手动定义 promise_type 和调度逻辑,学习曲线陡峭。

2.标准库支持不足:C++20 没有内置事件循环或调度器,需依赖第三方库。

3.内存管理:协程帧在堆上分配,可能增加内存开销。

4.调试困难:状态机转换和协程帧管理可能导致调试复杂。

性能优化

避免在协程中分配过多内存,合理设计暂停点。

http://www.dtcms.com/a/295574.html

相关文章:

  • 移动端自动化Appium框架
  • Linux服务器安全自动化审计实战:一键扫描账户/网络/进程/计划任务风险(附开源脚本)
  • rna_seq_pipeline.py-python002
  • 同步时钟系统提升仓库自动化水平
  • 服务器启动日志等级
  • 锁定锁存器 | 原理 / 应用 / 时序
  • 无广告终端安全产品推荐:打造纯净办公环境的安全之选
  • 【Spring Cloud Gateway 实战系列】终极篇:演进方向与未来架构
  • Gitea——私有git服务器搭建教程
  • AWS云S3+Glue+EMRonEC2+ReadShift
  • RK3568笔记九十一:QT环境搭建
  • 2025创新杯(钉钉杯)数学建模 AB赛题已出
  • ESP32S3 Ubuntu vscode如何使用USB-JTAG调试
  • VR全景制作的流程?VR全景制作可以用在哪些领域?
  • 【算法】分治
  • Ubuntu 20.04 上安装 SPDK
  • RP2040关键汇编函数解释
  • 旧物回收小程序系统开发——开启绿色生活新篇章
  • 基于区块链的商品销售系统(fiscobcos)
  • 本地部署dify1.7.0流程-windows docker
  • [AI 生成] Flink 面试题
  • 企业ERP系统全模块深度解析:从基础管理到智能运营
  • 算法提升之字符串(字典树)
  • 【C++】标准模板库(STL)—— 学习算法的利器
  • 【Qt开发】信号与槽(一)
  • 【MediaTek】AN7563编译wlan_hwifi出现en_npu.c:42:10: fatal error:
  • 上课啦 | 7月27日 Oracle OCP 19C(直播/面授 )
  • docker pull weaviate 国内拉取失败的问题
  • 面试题(技术面+hr面)
  • odoo欧度软件小程序——删除用户