背景

现代编程离不开异步。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 对比

PythonC++
async defTask<T> 返回类型
awaitco_await
return valueco_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;
}

坑点

  1. 协程不能用在模板参数里std::vector<Task<int>> 可以,但 Task<Task<int>> 不行
  2. 生命周期管理 — 协程句柄必须手动 destroy(),除非用 RAII 封装
  3. 调试困难 — 栈帧被编译器切分,gdb 支持还在完善中

总结

C++20 协程还很年轻,库支持不如 Python 完善。但对于构建高性能网络服务,它的零成本抽象是其他语言难以比拟的。

学习曲线: Python asyncio → C++ 协程,会发现语言虽不同,思维是相通的。


我的项目里更多用 Go 做网络并发,C++ 协程主要用于极致性能场景。