在 C++20 中,协程(Coroutine)被正式引入标准库,提供了一种新的异步编程模型,既可以写出像同步代码一样直观的逻辑,又能高效地管理资源。本文从协程的基本概念入手,逐步演示如何使用 std::generator 和 std::task,并给出一个完整的网络请求与文件写入的实战案例,帮助读者快速上手。
一、协程概述
协程是一种可挂起的函数,调用时并不会立即执行完毕,而是可以在执行过程中“挂起”,等待某些条件满足后再恢复。C++20 通过关键字 co_await, co_yield, co_return 实现协程的挂起、产生值和返回结果。
co_await:等待一个可等待对象(Awaitable),暂停协程直到其完成。
co_yield:产生一个值,暂停协程直到下一个 co_yield 或协程结束。
co_return:结束协程并返回值。
二、核心类型
2.1 std::generator
`std::generator
` 是一个可迭代的协程,适合需要按需产生一系列值的场景。其实现方式类似于 `std::vector`,但内存消耗更低,因为只在需要时生成值。
“`cpp
std::generator
range(int start, int end) {
for (int i = start; i
`std::task
` 用于表示异步操作,类似于 `std::future`,但不阻塞线程。它支持 `co_await` 等待操作完成,并可以链式调用。
“`cpp
std::task async_read(const std::string& path) {
std::ifstream ifs(path);
std::string content((std::istreambuf_iterator
(ifs)),
std::istreambuf_iterator
());
co_return content;
}
“`
## 三、实战案例:异步 HTTP GET 与文件写入
下面给出一个完整的示例,演示如何使用协程实现一个异步 HTTP GET 请求,然后将结果写入磁盘。为简化实现,使用 `asio`(Boost.Asio)作为网络库,并在 C++20 标准协程基础上封装 `asio::awaitable`。
### 3.1 环境准备
“`bash
# 安装 Boost 和 ASIO(单头文件版本可直接使用)
sudo apt-get install libboost-dev libboost-system-dev libboost-thread-dev
“`
### 3.2 代码实现
“`cpp
#include
#include
#include
#include
#include
#include
#include
using asio::awaitable;
using asio::ip::tcp;
using asio::use_awaitable;
using namespace std::chrono_literals;
// 异步 HTTP GET 请求
awaitable http_get(const std::string& host, const std::string& path) {
auto executor = co_await asio::this_coro::executor;
tcp::resolver resolver(executor);
auto endpoints = co_await resolver.async_resolve(host, “http”, use_awaitable);
tcp::socket socket(executor);
co_await asio::async_connect(socket, endpoints, use_awaitable);
// 构造请求
std::string request = “GET ” + path + ” HTTP/1.1\r\n”;
request += “Host: ” + host + “\r\n”;
request += “Connection: close\r\n\r\n”;
co_await asio::async_write(socket, asio::buffer(request), use_awaitable);
// 读取响应
std::string response;
asio::streambuf buffer;
while (true) {
std::size_t n = co_await asio::async_read(socket, buffer, asio::transfer_at_least(1), use_awaitable);
if (n == 0) break;
std::istream is(&buffer);
std::string line;
std::getline(is, line);
response += line + “\n”;
}
co_return response;
}
// 异步文件写入
awaitable
async_write_file(const std::string& path, const std::string& data) {
auto executor = co_await asio::this_coro::executor;
asio::steady_timer timer(executor, std::chrono::seconds(1)); // 模拟异步写入
co_await timer.async_wait(use_awaitable);
std::ofstream ofs(path, std::ios::binary);
ofs.write(data.c_str(), data.size());
ofs.close();
}
// 主协程
awaitable
main_coro() {
try {
std::string host = “example.com”;
std::string path = “/”;
std::cout