输入关键词开始搜索

C++20 协程 — Coroutines

三大关键字

co_await expr   // 暂停,等待异步操作完成
co_yield expr   // 暂停,产出一个值(generator)
co_return expr  // 结束协程,返回最终值

任何含这三个关键字之一的函数就是协程——返回值必须是满足协程约束的类型。

最简单的 Generator

#include <coroutine>
#include <iostream>

template <typename T>
struct Generator {
    struct promise_type {
        T current;
        Generator get_return_object() {
            return Generator{Handle::from_promise(*this)};
        }
        std::suspend_always initial_suspend() { return {}; }
        std::suspend_always final_suspend() noexcept { return {}; }
        std::suspend_always yield_value(T v) { current = v; return {}; }
        void return_void() {}
        void unhandled_exception() { std::terminate(); }
    };
    using Handle = std::coroutine_handle<promise_type>;
    Handle h;
    explicit Generator(Handle h) : h(h) {}
    ~Generator() { if (h) h.destroy(); }
    Generator(const Generator &) = delete;
    Generator(Generator &&o) : h(o.h) { o.h = nullptr; }

    bool next() {
        if (!h || h.done()) return false;
        h.resume();
        return !h.done();
    }
    T value() { return h.promise().current; }
};

// 使用
Generator<int> range(int n) {
    for (int i = 0; i < n; ++i)
        co_yield i;  // 暂停,产出 i,等 next() 再恢复
}

int main() {
    auto gen = range(5);
    while (gen.next())
        std::cout << gen.value() << ' ';  // 0 1 2 3 4
}

协程的生命周期

1. 分配协程帧(堆上,编译器可能优化到栈)
2. 调用 promise_type::get_return_object() → 返回给调用者
3. initial_suspend → 暂停还是立即执行
4. 协程体执行(co_await / co_yield / co_return)
5. final_suspend → 暂停等待销毁或自动销毁
6. promise 析构 + 协程帧释放

简单 Task(异步替代 future)

struct Task {
    struct promise_type {
        Task get_return_object() { return Task{Handle::from_promise(*this)}; }
        std::suspend_never initial_suspend() { return {}; } // 立即开始
        std::suspend_always final_suspend() noexcept { return {}; }
        void return_void() {}
        void unhandled_exception() { std::terminate(); }
    };
    using Handle = std::coroutine_handle<promise_type>;
    Handle h;
    ~Task() { if (h) h.destroy(); }

    bool done() { return h.done(); }
    void resume() { h.resume(); }
};

Task asyncWork() {
    std::cout << "Step 1\n";
    co_await std::suspend_always{};  // 暂停
    std::cout << "Step 2\n";
}

int main() {
    auto task = asyncWork();  // Step 1(initial_suspend_never 立即执行)
    std::cout << "Main says hi\n";
    task.resume();            // Step 2
}
// 输出: Step 1 → Main says hi → Step 2

协程的实际价值

// ❌ 传统异步 — 回调地狱
fetchData([this](Data d) {
    processData(d, [this](Result r) {
        saveResult(r, [this]() {
            updateUI();
        });
    });
});

// ✅ 协程 — 像同步代码一样写异步逻辑
Task loadAndProcess() {
    Data d = co_await fetchData();
    Result r = co_await processData(d);
    co_await saveResult(r);
    updateUI();
}

关键概念

概念说明
promise_type协程内部状态,控制协程行为
coroutine_handle协程的外部句柄,可以 resume/destroy
initial_suspend刚进协程时的行为(立即开始 vs 暂停等待)
final_suspend结束时的行为(自动销毁 vs 让外部手动销毁)
协程帧存放局部变量和 promise 的堆内存

常见陷阱

// 陷阱 1: 协程帧在堆上分配 → 可能引发 malloc
// 编译器有时能优化(HALO — Heap Allocation eLision Optimization)
// 但不保证 → 高频调用要测试

// 陷阱 2: 引用参数可能悬空
Task bad(const std::string &s) {
    co_await something();
    std::cout << s;  // s 可能已析构!
}
// ✅ 按值传参或确保生命周期

// 陷阱 3: co_await 只能在协程内使用
// co_await expr 只能在 async 函数中