欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 文旅 > 旅游 > 协程 Coroutine

协程 Coroutine

2025/4/2 3:34:59 来源:https://blog.csdn.net/JK01WYX/article/details/146770091  浏览:    关键词:协程 Coroutine

协程是 C++20 引入的新特性。

文章目录

  • 基本概念
      • std::coroutine_handle
      • promise 类型
      • co_yield
    • 基本用法
  • 优势
      • 异步 TCP
      • co_await

基本概念

协程(Coroutine)是一种比线程更加轻量级的并发编程模型。协程的调度由程序员手动控制。

异步不是并行,但是不阻塞~

协程的上下文切换是在用户态完成的,不需要陷入内核,因此切换速度非常快。
当协程遇到 co_await 时,它会保存当前的上下文信息,然后让出执行权,待异步操作完成后再恢复上下文继续执行。切换开销非常小,几乎可以忽略不计。

  • 如果你在做异步编程(如网络 I/O、文件 I/O),用 co_await

  • 如果你在实现生成器(如迭代器、流式数据处理),用 co_yield

std::coroutine_handle

协程的句柄,一个模板类——对协程进行控制与管理。

std::coroutine_handle<promise_type>;

主要方法:

  • resume():用于让协程恢复执行。
    当协程处于挂起状态时,调用 resume() 方法,协程就会从挂起的位置接着执行。

  • done():用于检查协程是否已经执行完毕。
    若协程执行完毕,done() 方法会返回 true;反之,则返回 false。

  • destroy():用于销毁协程,释放协程占用的资源。

  • promise():返回与协程关联的 promise 对象(struct promise_type),通过这个对象可以访问协程的状态和数据。

promise 类型

promise_type 对象定义了协程的行为和状态管理方式。

promise_type 结构体中的部分方法名字是固定的,协程框架会依据这些固定的方法名来调用相应的逻辑

固定的方法名:

  • get_return_object 创建并返回协程的返回对象
  • yield_value 当协程里使用 【co_yield】 语句时,会调用此方法。它负责处理 co_yield 后面的值,并通过返回值决定协程是否要挂起。
  • initial_suspend 在协程启动时会调用此方法,决定了协程在开始执行时是否要立即挂起。
    返回类型】有 std::suspend_always(表示协程开始就挂起,控制权交还给调用者)和 std::suspend_never(表示协程开始就执行,co_yield 后接着执行)
  • final_suspend 协程即将结束时会调用该方法,决定了协程在结束时是否要挂起。
    返回同 initial_suspend
  • unhandled_exception 当协程内部抛出未被内部捕获的异常时,会调用这个方法来处理异常。
  • return_voidreturn_value 处理协程返回值。

co_yield

co_yield 用于在协程中产生一个值,然后暂停协程的执行,将控制权交还给调用者。

这意味着协程可以在不同的时间点多次产生值,而不是像普通函数那样一次性返回一个结果
当调用者再次请求协程继续执行时,协程会从 co_yield 之后的位置恢复执行。

避免了传统异步编程中的回调地狱问题。

Generator generate_numbers() {for (int i = 0; i < 5; ++i) {co_yield i; // 生成值并挂起}
}

每执行一次 co_yield i,协程框架就会调用 Generator 类(当前协程函数的返回值类型)中 promise_type 结构体yield_value 方法,并且把 i 的值传递给该方法。

基本用法

简单生成器示例,生成从 0 到 4 的整数

#include <coroutine>
#include <iostream>
#include <cstdlib>// 生成器类定义
class Generator {
public:struct promise_type { // 【定义了协程的行为】 (包括如何初始化、挂起、恢复、处理异常以及产生值等)int current_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 unhandled_exception() { std::exit(1); } // 异常处理std::suspend_always yield_value(int value) { // ※ 用于处理 co_yield 语句current_value = value;return {};}void return_void() {} // 处理 co_return 语句,这里的协程不返回值。};using handle_type = std::coroutine_handle<promise_type>;//协程的句柄,对协程进行控制与管理// 迭代器类struct Iterator {handle_type coro_handle;bool operator!=(const Iterator&) const { return !coro_handle.done(); }void operator++() { coro_handle.resume(); }int operator*() const { return coro_handle.promise().current_value; } // 获取值};// 构造函数和析构函数 : 接收/销毁 句柄explicit Generator(handle_type h) : coro_handle(h) {}~Generator() { if (coro_handle) coro_handle.destroy(); }// 支持范围循环Iterator begin() {if (coro_handle) coro_handle.resume();return Iterator{coro_handle};}Iterator end() { return Iterator{nullptr}; }private:handle_type coro_handle;
};// 协程函数
Generator generate_numbers() {for (int i = 0; i < 5; ++i) {co_yield i; // 生成值并挂起}
}// 使用示例
int main() {for (auto num : generate_numbers()) {std::cout << "Generated: " << num << std::endl;}return 0;
}

优势

  • 异步非阻塞:
    协程通过 co_await 挂起执行但不阻塞线程,可以在等待 I/O 时释放 CPU 资源
    单线程即可处理大量并发 I/O 请求(如网络连接、文件读写)

像网络IO,磁盘IO等,CPU只是调度,真正执行的是硬件:网卡和磁盘。
所以程序中高IO的情况下,不想阻塞IO,就可以使用协程。

生命周期结束,协程可以正常进行。
IO异常后,协程也会抛出异常。

  • 低开销
    协程切换成本远低于线程切换(通常是纳秒级 vs 微秒级)。

异步 TCP

#include <boost/asio.hpp>
#include <boost/asio/use_awaitable.hpp>
#include <coroutine>
#include <iostream>namespace asio = boost::asio;
using asio::ip::tcp;// 协程异步连接和读写
asio::awaitable<void> async_client() { // 该协程不返回任何值try {auto executor = co_await asio::this_coro::executor; //获取当前协程的执行器,用于创建 tcp::socket 对象for (int i = 0; i < 5; ++i) {tcp::socket socket(executor);// 异步连接(非阻塞)co_await socket.async_connect( // 【异步连接】tcp::endpoint(asio::ip::make_address("127.0.0.1"), 8080),asio::use_awaitable // 协程挂起等, 控制权返回给事件循环,事件循环可以继续处理其他异步操作。);// 异步发送数据std::string request = "Hello from coroutine!";co_await asio::async_write(socket, asio::buffer(request), asio::use_awaitable); // 【异步发送】// 异步接收响应char response[1024];size_t len = co_await socket.async_read_some(asio::buffer(response), asio::use_awaitable); // 【异步接收】std::cout << "Received: " << std::string(response, len) << std::endl;}} catch (const std::exception& e) {std::cerr << "Error: " << e.what() << std::endl;}
}int main() {asio::io_context io_context; // Boost.Asio 的核心对象,负责管理所有的异步操作和事件循环。for (int i = 0; i < 5; ++i) {// 启动协程任务asio::co_spawn(io_context, async_client(), asio::detached); //           (执行器,     协程函数,      协程任务独立运行 不等待)}// 运行事件循环io_context.run(); return 0;
}

co_await

关键字。让异步代码可以以同步的形式编写。

C++20 之前,没有 co_await 这个关键字,异步编程通常借助回调函数或者 std::future 与 std::promise 来实现,难以维护。

后面通常跟着一个可等待对象(Awaitable),这个对象需要实现特定的接口:

  • await_ready 判断可等待对象是否已经准备好
  • await_suspend 当 await_ready 返回 false 时,await_suspend 方法会被调用。这个方法负责挂起协程,并安排在可等待对象完成时恢复协程的执行。
  • await_resume 当可等待对象完成时,await_resume 方法会被调用。该方法的返回值会作为 co_await 表达式的结果返回给协程。
#include <iostream>
#include <coroutine>
#include <future>
#include <thread>// 定义一个可等待对象
struct Awaitable {bool await_ready() const noexcept { return false; }void await_suspend(std::coroutine_handle<> handle) const noexcept {std::thread([handle]() {std::this_thread::sleep_for(std::chrono::seconds(1));handle.resume();}).detach();}void await_resume() const noexcept {}
};// 协程函数
std::coroutine_handle<> coroutine_handle; // 协程句柄声明,这里没有使用
struct Task { // 协程函数的返回类型, 同“基本用法”的Generator类struct promise_type {Task get_return_object() { return {}; }std::suspend_never initial_suspend() { return {}; }std::suspend_never final_suspend() noexcept { return {}; }void return_void() {}void unhandled_exception() {}};
};Task coroutine_function() {std::cout << "Coroutine started." << std::endl;co_await Awaitable{};std::cout << "Coroutine resumed after waiting." << std::endl;
}int main() {coroutine_function();std::cout << "Main function continues." << std::endl;std::this_thread::sleep_for(std::chrono::seconds(2));return 0;
}

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com

热搜词