强曰为道
与天地相似,故不违。知周乎万物,而道济天下,故不过。旁行而不流,乐天知命,故不忧.
文档目录

异步与协程精讲 / 第12章:纤程 —— C++ 的协程之路

第12章:纤程 —— C++ 的协程之路

12.1 什么是纤程?

纤程(Fiber)是一种用户态的轻量级线程,由应用程序(而非操作系统内核)负责调度。纤程的概念介于线程和协程之间:

特性 线程(Thread) 纤程(Fiber) 协程(Coroutine)
调度方 OS 内核 运行时库 编译器/程序员
OS 管理 独立栈(较小) 无栈或共享栈
切换开销 高(内核态切换) 中(用户态栈切换) 低(状态机跳转)
抢占 支持 不支持(协作式) 不支持

纤程的历史

  • Windows Fiber API(Windows 2000+):CreateFiber()SwitchToFiber()
  • Boost.Fiber:跨平台 C++ 纤程库
  • ucontext(POSIX):getcontext()setcontext()swapcontext()
  • C++20 Coroutines:语言级别的协程支持

12.2 C++20 协程基础

核心概念

C++20 引入了三个新的关键字:

关键字 用途 类比
co_await 暂停执行,等待结果 JavaScript 的 await
co_yield 产出值并暂停 Python 的 yield
co_return 返回最终结果 return

生成器(Generator)

#include <coroutine>
#include <iostream>
#include <optional>

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

    std::coroutine_handle<promise_type> handle;

    ~Generator() {
        if (handle) handle.destroy();
    }

    std::optional<T> next() {
        if (!handle || handle.done()) return std::nullopt;
        handle.resume();
        return handle.promise().value;
    }
};

// 使用
Generator<int> fibonacci() {
    int a = 0, b = 1;
    while (true) {
        co_yield a;
        auto temp = a;
        a = b;
        b = temp + b;
    }
}

int main() {
    auto fib = fibonacci();
    for (int i = 0; i < 10; ++i) {
        std::cout << *fib.next() << " ";
    }
    // 输出: 0 1 1 2 3 5 8 13 21 34
}

12.3 co_await 与 Awaitable

Awaitable 接口

struct Awaitable {
    // 是否需要暂停
    bool await_ready() const noexcept { return false; }
    
    // 暂停时调用,可以注册回调
    void await_suspend(std::coroutine_handle<> h) const {
        // 注册回调,异步操作完成后调用 h.resume()
    }
    
    // 恢复时返回值
    int await_resume() const { return 42; }
};

Task 类型

#include <coroutine>
#include <future>

struct Task {
    struct promise_type {
        int result;
        
        Task get_return_object() {
            return Task{
                std::coroutine_handle<promise_type>::from_promise(*this)
            };
        }
        
        std::suspend_never initial_suspend() { return {}; }
        std::suspend_always final_suspend() noexcept { return {}; }
        void return_value(int v) { result = v; }
        void unhandled_exception() { std::terminate(); }
    };

    std::coroutine_handle<promise_type> handle;
    
    int get_result() { return handle.promise().result; }
};

// 使用 co_await
Task async_compute() {
    int a = co_await some_async_operation();
    int b = co_await another_async_operation(a);
    co_return a + b;
}

12.4 Boost.Fiber

基本用法

#include <boost/fiber/all.hpp>
#include <iostream>

void fiber_fn(int id) {
    for (int i = 0; i < 5; ++i) {
        std::cout << "Fiber " << id << " step " << i << "\n";
        boost::fibers::yield();  // 让出 CPU
    }
}

int main() {
    boost::fibers::fiber f1(fiber_fn, 1);
    boost::fibers::fiber f2(fiber_fn, 2);
    
    f1.join();
    f2.join();
}

Fiber Channel(类似 Go Channel)

#include <boost/fiber/all.hpp>

using Channel = boost::fibers::buffered_channel<int>;

void producer(Channel& ch) {
    for (int i = 0; i < 10; ++i) {
        ch.push(i);
    }
    ch.close();
}

void consumer(Channel& ch) {
    int value;
    while (ch.pop(value) != boost::fibers::channel_op_status::closed) {
        std::cout << "收到: " << value << "\n";
    }
}

int main() {
    Channel ch{10};
    boost::fibers::fiber f1(producer, std::ref(ch));
    boost::fibers::fiber f2(consumer, std::ref(ch));
    f1.join();
    f2.join();
}

12.5 协程框架对比

框架 类型 特点 适用场景
C++20 Coroutines 无栈协程 语言级支持,零成本抽象 通用
Boost.Fiber 有栈纤程 类似线程的编程模型 渐进式迁移
Boost.Coroutine2 有栈协程 底层控制,上下文切换 底层库
libco 有栈协程 腾讯开源,高性能 高性能服务器
libgo 有栈协程 类似 Go 的语法 Go 风格 C++ 开发
cppcoro 无栈协程 Lewis Baker 的库,C++20 前身 参考实现

12.6 性能对比

协程切换开销

方案 单次切换时间 内存占用(每单位) 最大数量
OS 线程 ~1-10μs ~1MB 数千
Boost.Fiber ~100ns ~64KB-1MB 数万
C++20 Coroutine ~10ns ~几十字节 数百万
libco ~50ns ~128KB 数十万

内存使用对比(100万并发)

方案 每单位内存 100万总计 可行性
OS 线程 1MB 1TB
Boost.Fiber 64KB 64GB ⚠️ 边界
C++20 Coroutine 256B 256MB

12.7 业务场景:高性能网络服务器

#include <boost/asio.hpp>
#include <coroutine>

using boost::asio::ip::tcp;

struct Session : std::enable_shared_from_this<Session> {
    tcp::socket socket;
    char data[1024];
    
    Session(tcp::socket s) : socket(std::move(s)) {}
    
    void start() { do_read(); }
    
    void do_read() {
        auto self = shared_from_this();
        socket.async_read_some(
            boost::asio::buffer(data),
            [this, self](boost::system::error_code ec, std::size_t length) {
                if (!ec) {
                    do_write(length);
                }
            }
        );
    }
    
    void do_write(std::size_t length) {
        auto self = shared_from_this();
        boost::asio::async_write(
            socket,
            boost::asio::buffer(data, length),
            [this, self](boost::system::error_code, std::size_t) {
                do_read();
            }
        );
    }
};

12.8 本章小结

要点 说明
纤程定义 用户态轻量级线程,协作式调度
C++20 协程 co_await/co_yield/co_return,无栈协程
Boost.Fiber 有栈纤程,类似线程的编程模型
性能 协程切换 ~10ns,内存占用极小
选择建议 新项目用 C++20 协程,渐进迁移用 Boost.Fiber

下一章预告:绿色线程——为什么它在 2000 年代消失了,又在 2020 年代回归了?


扩展阅读