背景
现代编程离不开异步。Python 有 asyncio,JavaScript 有 async/await,Go 有 goroutine。C++20 终于引入了协程(Coroutines)。
今天从 Python asyncio 的视角来看看 C++ 协程怎么用。
Python asyncio 的模式
async def fetch(url: str) -> str:
# 模拟异步IO
await asyncio.sleep(1)
return f"data from {url}"
async def main():
results = await asyncio.gather(
fetch("a.com"),
fetch("b.com"),
fetch("c.com"),
)
print(results)
asyncio.run(main())
核心概念:async def 定义协程函数,await 挂起等待,asyncio.gather 并发执行。
C++20 协程入门
C++ 协程的关键类型:
co_await— 挂起协程co_return— 返回值(相当于return)co_yield— 产出值(用于生成器)std::suspend_never/std::suspend_always— 挂起策略
简单例子:模拟异步任务
#include <coroutine>
#include <future>
#include <iostream>
template<typename T>
struct Task {
struct promise_type {
T value;
std::exception_ptr error;
auto get_return_object() {
return Task{std::coroutine_handle<promise_type>::from_promise(*this)};
}
auto initial_suspend() { return std::suspend_never{}; }
auto final_suspend() noexcept { return std::suspend_always{}; }
void return_value(T v) { value = v; }
void unhandled_exception() { error = std::current_exception(); }
};
std::coroutine_handle<promise_type> handle;
Task(std::coroutine_handle<promise_type> h) : handle(h) {}
~Task() { if (handle) handle.destroy(); }
T get() {
handle.resume();
return handle.promise().value;
}
};
// 模拟异步操作
Task<int> async_fetch() {
std::cout << "开始异步任务...\n";
co_await std::suspend_always{}; // 挂起,模拟异步等待
std::cout << "异步任务完成\n";
co_return 42;
}
int main() {
auto task = async_fetch();
std::cout << "做一些其他事情...\n";
int result = task.get();
std::cout << "结果: " << result << "\n";
}
和 Python asyncio 对比
| Python | C++ |
|---|---|
async def | Task<T> 返回类型 |
await | co_await |
return value | co_return value |
asyncio.sleep(1) | std::suspend_always{} |
| 事件循环自动调度 | 手动 resume() |
实际应用:HTTP 客户端
Task<Response> http_get(const std::string& url) {
co_await socket_.async_connect(url);
co_await socket_.async_write(request_);
Response resp = co_await socket_.async_read();
co_return resp;
}
Task<std::vector<Response>> fetch_all(const std::vector<std::string>& urls) {
std::vector<Task<Response>> tasks;
for (const auto& url : urls) {
tasks.push_back(http_get(url));
}
std::vector<Response> results;
for (auto& task : tasks) {
results.push_back(task.get());
}
co_return results;
}
坑点
- 协程不能用在模板参数里 —
std::vector<Task<int>>可以,但Task<Task<int>>不行 - 生命周期管理 — 协程句柄必须手动
destroy(),除非用 RAII 封装 - 调试困难 — 栈帧被编译器切分,gdb 支持还在完善中
总结
C++20 协程还很年轻,库支持不如 Python 完善。但对于构建高性能网络服务,它的零成本抽象是其他语言难以比拟的。
学习曲线: Python asyncio → C++ 协程,会发现语言虽不同,思维是相通的。
我的项目里更多用 Go 做网络并发,C++ 协程主要用于极致性能场景。