C++20协程:从概念到实战

C++20引入的协程(Coroutines)提供了一种轻量级的异步编程模型,使得编写顺序化的非阻塞代码变得更为直观。与传统回调、future/async或线程相比,协程可以在同一线程中暂停与恢复执行,从而避免了上下文切换成本并简化了错误处理。下面从协程的核心概念、实现细节到一个完整的异步文件读取案例,逐步揭示协程在C++中的魅力。

1. 协程的核心概念

1.1 生成器(Generator)

生成器是一种特殊的协程,每次调用co_yield会返回一个值,并在下一次调用时继续执行。它类似于Python的yield语法。

1.2 任务(Task)

任务是一种可等待的协程,使用co_return返回最终结果。任务本身实现了std::future的接口,支持awaitable操作。

1.3 处理器(Awaiter)

协程体内出现co_await时会创建一个awaiter对象,负责决定协程是否需要挂起以及挂起时的恢复方式。标准库提供了如std::suspend_alwaysstd::suspend_never以及std::future等实现。

2. 协程的实现细节

2.1 协程句柄

C++标准库使用`std::coroutine_handle

`来保存协程的状态。每个协程都有一个对应的Promise类型,负责协程结果的包装。 ### 2.2 Promise对象 Promise对象在协程创建时分配,负责: – 保存协程返回值或异常。 – 提供`get_return_object`返回协程的句柄。 – 定义`final_suspend`决定协程结束后是否需要挂起。 ### 2.3 挂起与恢复 – `co_await`:会调用awaiter的`await_ready`、`await_suspend`和`await_resume`。 – `co_yield`:将生成器的值传递给调用方,同时挂起协程。 – `co_return`:将结果存入Promise,然后挂起协程直至`final_suspend`完成。 ## 3. 一个完整的异步文件读取案例 下面演示如何使用协程实现一个非阻塞的文件读取工具。我们利用Boost.Asio的异步 I/O 与 C++20协程无缝集成。 “`cpp #include #include #include #include #include #include #include using namespace boost::asio; using boost::asio::awaitable; using namespace std::chrono_literals; // 简单的异步文件读取函数 awaitable async_read_file(io_context& ctx, const std::string& path) { co_await this_coro::yield; // 协程入口,确保异步上下文已准备 // 打开文件 std::ifstream file(path, std::ios::binary | std::ios::ate); if (!file) { throw std::runtime_error(“Failed to open file: ” + path); } // 获取文件大小 std::streamsize size = file.tellg(); file.seekg(0, std::ios::beg); // 预留缓冲区 std::string buffer(static_cast (size), ‘\0’); co_await async_read_some(file, buffer.data(), static_cast(size), use_awaitable); co_return buffer; } // 主程序 int main() { io_context ctx; // 启动协程任务 co_spawn(ctx, [&ctx]() -> awaitable { try { std::string content = co_await async_read_file(ctx, “sample.txt”); std::cout

发表评论