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

异步与协程精讲 / 第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 线程1MB1TB
Boost.Fiber64KB64GB⚠️ 边界
C++20 Coroutine256B256MB

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 年代回归了?


扩展阅读