悠悠楠杉
C++协程基础与生成器实现
在现代C++开发中,协程(Coroutine)作为一种轻量级的并发编程模型,正逐渐被开发者所重视。尤其是在需要延迟计算、逐个产生数据的场景下,生成器(Generator)模式显得尤为实用。本文将带你从零开始,使用C++20标准中的协程特性,实现一个简单的生成器,并深入理解其背后的工作机制。
传统的函数一旦返回,其执行上下文就会被销毁,无法再次从中断处继续执行。而协程则不同,它可以在运行过程中通过co_yield暂停自身,保留当前的状态,待下一次调用时从中断点恢复执行。这种能力使得协程非常适合用于实现惰性求值的数据流,比如无限序列、大文件逐行读取或网络数据流处理等。
要实现一个生成器,首先我们需要定义一个符合协程规范的返回类型。这个类型必须包含一个名为promise_type的嵌套类型,它是协程状态管理的核心。我们以一个返回整数的生成器为例:
cpp
include
include
struct Generator {
struct promisetype {
int currentvalue;
std::suspend_always initial_suspend() { return {}; }
std::suspend_always final_suspend() noexcept { return {}; }
Generator get_return_object() { return Generator{this}; }
void return_void() {}
std::suspend_always yield_value(int value) {
current_value = value;
return {};
}
void unhandled_exception() { std::terminate(); }
};
using handle_type = std::coroutine_handle<promise_type>;
explicit Generator(promise_type* p)
: coro(handle_type::from_promise(*p)) {}
~Generator() {
if (coro) coro.destroy();
}
Generator(const Generator&) = delete;
Generator& operator=(const Generator&) = delete;
Generator(Generator&& other) noexcept
: coro(other.coro) {
other.coro = nullptr;
}
Generator& operator=(Generator&& other) noexcept {
if (this != &other) {
if (coro) coro.destroy();
coro = other.coro;
other.coro = nullptr;
}
return *this;
}
int value() const { return coro.promise().current_value; }
bool move_next() {
if (!coro || coro.done()) return false;
coro.resume();
return !coro.done();
}
private:
handle_type coro;
};
上述代码中,promise_type定义了协程的行为:yield_value在每次调用co_yield时被触发,保存当前值并挂起;get_return_object返回供外部使用的生成器对象;而initial_suspend和final_suspend控制协程启动和结束时是否挂起。
接下来,我们可以编写一个使用该生成器的函数,例如生成斐波那契数列:
cpp
Generator fibonacci() {
int a = 0, b = 1;
while (true) {
co_yield a;
int next = a + b;
a = b;
b = next;
}
}
使用方式非常直观:
cpp
int main() {
auto gen = fibonacci();
for (int i = 0; i < 10; ++i) {
if (gen.move_next()) {
std::cout << gen.value() << " ";
}
}
std::cout << std::endl;
return 0;
}
输出结果为:0 1 1 2 3 5 8 13 21 34
整个过程无需预计算所有值,而是按需生成,节省内存且响应迅速。这正是生成器的魅力所在。
值得注意的是,C++20的协程是无栈协程(stackless coroutine),这意味着它们不能在任意嵌套层级中挂起,只能在最外层的协程函数中使用co_yield、co_await等关键字。但这也带来了更高的性能和更低的开销。
此外,生成器对象的生命周期管理至关重要。由于协程状态由堆上分配的帧维护,我们必须确保在不再需要时正确销毁句柄,避免资源泄漏。上面的移动语义设计正是为此服务。
总结来说,C++20协程为实现高效、优雅的生成器提供了语言级别的支持。通过合理设计promise_type和协程句柄的封装,我们可以轻松构建出功能强大且易于使用的惰性序列生成工具。随着编译器对协程支持的日益成熟,这类技术将在实际项目中发挥越来越重要的作用。

