异步与协程精讲 / 第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 年代回归了?
扩展阅读