C++20协程:实现原理与实战案例

在 C++20 中,协程(coroutine)被正式引入标准库,成为解决异步编程、生成器和协作式多任务等问题的强大工具。本文将从协程的基本概念、实现原理、关键标准库组件以及一个完整的实战案例入手,帮助你快速掌握并使用 C++20 协程。

1. 协程的基本概念

协程是一种轻量级的程序单元,可以在执行过程中暂停(co_awaitco_yieldco_return)并在未来某个点恢复。与线程不同,协程不需要独立的栈,而是共享调用栈,因而上下文切换开销极低。协程在 IO、网络、游戏循环等需要大量等待的场景中表现尤为突出。

2. C++20 协程的核心语法

  • co_await:挂起协程,等待一个 awaitable 对象完成后恢复。
  • co_yield:生成一个值,并挂起协程,等到下一个 co_awaitresume 再恢复。
  • co_return:返回协程最终结果并结束协程。

这些关键字与 std::experimental::coroutine 或者更现代的 std::coroutine 标准库组件配合使用,构成完整的协程体系。

3. 关键标准库组件

组件 作用
std::coroutine_handle 协程句柄,管理协程生命周期、恢复和销毁
std::suspend_always / std::suspend_never 控制协程挂起行为
std::promise / std::future 与协程配合实现异步结果返回
std::generator(C++23) 生成器类型,简化 co_yield 用法

在 C++20,最常见的做法是自己实现一个 awaitable 类型,然后在 operator co_await 中返回适配器,或者使用第三方库如 cppcoroBoost.Coroutine

4. 实战案例:异步文件读取

下面给出一个完整示例:使用协程实现异步读取文本文件,每行返回给调用者。示例中使用 asio 进行异步 I/O(或可自行替换为 boost::asio)。

#include <iostream>
#include <string>
#include <vector>
#include <filesystem>
#include <asio.hpp>
#include <coroutine>

using asio::awaitable;
using asio::io_context;
using asio::buffer;
using asio::use_awaitable;

// 简易异步读取一行
awaitable<std::string> async_read_line(asio::streambuf& sbuf, std::istream& in) {
    std::size_t n = co_await asio::async_read_until(in, sbuf, '\n', use_awaitable);
    std::istream is(&sbuf);
    std::string line;
    std::getline(is, line);
    co_return line;
}

// 主协程:读取文件所有行
awaitable<std::vector<std::string>> read_file_lines(const std::string& path) {
    asio::io_context& ctx = co_await asio::this_coro::executor;
    asio::posix::stream_descriptor fd(ctx, ::open(path.c_str(), O_RDONLY));
    std::istream in(&fd);
    asio::streambuf sbuf;
    std::vector<std::string> lines;
    while (true) {
        std::string line = co_await async_read_line(sbuf, in);
        if (line.empty() && in.eof()) break; // EOF
        lines.push_back(line);
    }
    co_return lines;
}

int main() {
    io_context ctx;
    std::string filename = "sample.txt";

    // 调用协程并等待结果
    auto future = std::async(std::launch::async, [&](){
        return co_spawn(ctx, read_file_lines(filename), asio::use_future).get();
    });

    // 继续做其他工作,模拟并发
    std::cout << "主线程继续执行...\n";

    // 等待文件读取完成
    std::vector<std::string> lines = future.get();

    std::cout << "文件读取完成,行数:" << lines.size() << "\n";
    for (const auto& l : lines) {
        std::cout << l << "\n";
    }
    return 0;
}

关键点解析

  1. awaitable:标记函数为协程返回类型,自动与 asio 的异步 API 集成。
  2. co_await:挂起当前协程,等待 async_read_until 完成。
  3. co_return:将结果返回给调用者。
  4. co_spawn:启动协程,并返回 std::future,与 asio 的事件循环配合使用。

5. 小结

  • 协程是 C++20 标准库中最具前瞻性的特性之一,适用于需要大量挂起与恢复的异步场景。
  • 通过 std::coroutine_handlestd::suspend_always 等基础组件,你可以自行实现 awaitable 类型,实现自定义的异步行为。
  • asio 等网络/IO 库配合,可以轻松完成高性能异步 I/O、协程化网络服务等。
  • 进一步学习可关注 C++23 的 std::generatorstd::async 的协程改造等新功能。

协程正在改变 C++ 的异步编程范式,掌握它将让你在未来的项目中拥有更高效、更可读的代码。祝你编码愉快!

发表评论