2.6. 协程 (C++20)

2.6.1. 协程基础概念

协程是可以暂停和恢复执行的函数。C++20 提供了协程的底层支持。

2.6.1.1. 协程关键字

  • co_await - 暂停并等待异步操作完成

  • co_yield - 暂停并产生一个值

  • co_return - 完成协程并返回值

#include <coroutine>

// 一个简单的生成器
generator<int> iota(int start, int end) {
    for (int i = start; i < end; ++i) {
        co_yield i;  // 产生值并暂停
    }
}

// 一个异步任务
task<int> async_compute() {
    int result = co_await async_read();  // 暂停等待
    co_return result * 2;
}

2.6.2. 实现简单的 Generator

#include <coroutine>
#include <optional>

template<typename T>
class generator {
public:
    struct promise_type {
        T current_value;
        
        generator get_return_object() {
            return generator{
                std::coroutine_handle<promise_type>::from_promise(*this)
            };
        }
        
        std::suspend_always initial_suspend() noexcept { return {}; }
        std::suspend_always final_suspend() noexcept { return {}; }
        
        std::suspend_always yield_value(T value) {
            current_value = std::move(value);
            return {};
        }
        
        void return_void() {}
        void unhandled_exception() { std::terminate(); }
    };

    using handle_type = std::coroutine_handle<promise_type>;

    generator(handle_type h) : handle_(h) {}
    generator(generator&& other) noexcept : handle_(other.handle_) {
        other.handle_ = nullptr;
    }
    ~generator() {
        if (handle_) handle_.destroy();
    }

    // 迭代器支持
    struct iterator {
        handle_type handle;
        
        iterator& operator++() {
            handle.resume();
            return *this;
        }
        
        T& operator*() {
            return handle.promise().current_value;
        }
        
        bool operator==(std::default_sentinel_t) const {
            return handle.done();
        }
    };

    iterator begin() {
        handle_.resume();
        return {handle_};
    }
    
    std::default_sentinel_t end() { return {}; }

private:
    handle_type handle_;
};

// 使用示例
generator<int> range(int start, int end) {
    for (int i = start; i < end; ++i) {
        co_yield i;
    }
}

int main() {
    for (int n : range(1, 10)) {
        std::cout << n << " ";  // 1 2 3 4 5 6 7 8 9
    }
}

2.6.3. 实现简单的 Task

#include <coroutine>

template<typename T>
class task {
public:
    struct promise_type {
        T result;
        std::exception_ptr exception;
        
        task get_return_object() {
            return task{
                std::coroutine_handle<promise_type>::from_promise(*this)
            };
        }
        
        std::suspend_never initial_suspend() noexcept { return {}; }
        std::suspend_always final_suspend() noexcept { return {}; }
        
        void return_value(T value) {
            result = std::move(value);
        }
        
        void unhandled_exception() {
            exception = std::current_exception();
        }
    };

    using handle_type = std::coroutine_handle<promise_type>;

    task(handle_type h) : handle_(h) {}
    task(task&& other) noexcept : handle_(other.handle_) {
        other.handle_ = nullptr;
    }
    ~task() {
        if (handle_) handle_.destroy();
    }

    T get() {
        if (handle_.promise().exception) {
            std::rethrow_exception(handle_.promise().exception);
        }
        return std::move(handle_.promise().result);
    }

    // co_await 支持
    bool await_ready() const noexcept { return handle_.done(); }
    void await_suspend(std::coroutine_handle<>) const noexcept {}
    T await_resume() { return get(); }

private:
    handle_type handle_;
};

// 使用示例
task<int> compute_async() {
    co_return 42;
}

task<int> main_task() {
    int result = co_await compute_async();
    co_return result * 2;
}

2.6.4. 自定义 Awaitable

struct timer_awaitable {
    std::chrono::milliseconds duration;
    
    bool await_ready() const noexcept { return false; }
    
    void await_suspend(std::coroutine_handle<> handle) const {
        // 在另一个线程中等待并恢复
        std::thread([=] {
            std::this_thread::sleep_for(duration);
            handle.resume();
        }).detach();
    }
    
    void await_resume() const noexcept {}
};

auto sleep_for(std::chrono::milliseconds ms) {
    return timer_awaitable{ms};
}

task<void> delayed_task() {
    std::cout << "Starting...\n";
    co_await sleep_for(std::chrono::seconds(1));
    std::cout << "Done!\n";
}

2.6.5. 协程与异步 I/O

// 概念性示例:与异步 I/O 集成
class async_read_awaitable {
    int fd;
    char* buffer;
    size_t size;
    
public:
    async_read_awaitable(int fd, char* buf, size_t sz)
        : fd(fd), buffer(buf), size(sz) {}
    
    bool await_ready() const noexcept { return false; }
    
    void await_suspend(std::coroutine_handle<> handle) {
        // 注册到事件循环(如 epoll、io_uring)
        // 当读取完成时调用 handle.resume()
    }
    
    ssize_t await_resume() {
        // 返回读取的字节数
        return bytes_read;
    }
};

task<std::string> async_read_file(const std::string& path) {
    int fd = open(path.c_str(), O_RDONLY);
    std::string content;
    char buffer[4096];
    
    while (true) {
        ssize_t n = co_await async_read_awaitable(fd, buffer, sizeof(buffer));
        if (n <= 0) break;
        content.append(buffer, n);
    }
    
    close(fd);
    co_return content;
}

2.6.6. 协程使用库

由于 C++20 只提供了协程的底层机制,推荐使用成熟的协程库:

2.6.6.1. cppcoro

#include <cppcoro/task.hpp>
#include <cppcoro/sync_wait.hpp>
#include <cppcoro/when_all.hpp>

cppcoro::task<int> compute_a() { co_return 1; }
cppcoro::task<int> compute_b() { co_return 2; }

cppcoro::task<int> main_task() {
    auto [a, b] = co_await cppcoro::when_all(
        compute_a(),
        compute_b()
    );
    co_return a + b;
}

int main() {
    int result = cppcoro::sync_wait(main_task());
}

2.6.6.2. folly coro

#include <folly/experimental/coro/Task.h>
#include <folly/experimental/coro/BlockingWait.h>

folly::coro::Task<int> async_compute() {
    co_return 42;
}

int main() {
    int result = folly::coro::blockingWait(async_compute());
}

2.6.7. 协程最佳实践

2.6.7.1. 1. 避免在协程中使用引用参数

// 危险:引用可能在协程恢复前失效
task<void> dangerous(const std::string& s) {
    co_await some_async_op();
    std::cout << s;  // s 可能已无效
}

// 安全:按值传递
task<void> safe(std::string s) {
    co_await some_async_op();
    std::cout << s;  // OK
}

2.6.7.2. 2. 小心生命周期

// 危险:lambda 捕获悬空
void dangerous() {
    int value = 42;
    auto coro = [&]() -> task<void> {
        co_await some_async_op();
        std::cout << value;  // value 可能已销毁
    }();
}

// 安全:值捕获或确保生命周期
void safe() {
    int value = 42;
    auto coro = [value]() -> task<void> {
        co_await some_async_op();
        std::cout << value;  // OK
    }();
}

2.6.7.3. 3. 使用 RAII 管理协程句柄

class coroutine_owner {
    std::coroutine_handle<> handle_;
public:
    explicit coroutine_owner(std::coroutine_handle<> h) : handle_(h) {}
    ~coroutine_owner() {
        if (handle_) handle_.destroy();
    }
    // 移动语义...
};

备注

C++23 引入了 std::generator,提供了标准的生成器实现。 协程仍在快速发展中,建议关注最新的标准和库。