c++怎么实现一个简单的协程库_c++手写协程实现原理与示例

36次阅读

协程通过用户态上下文切换实现轻量级并发,本文基于c++11和ucontext库实现简易协程,展示创建、挂起与恢复机制,利用getcontext、makecontext和swapcontext完成隔离与执行流控制,两个协程可交替执行,体现协程核心原理。

c++怎么实现一个简单的协程库_c++手写协程实现原理与示例

协程是一种比线程更轻量的并发编程模型,能够在用户态实现函数的暂停与恢复。C++20 引入了原生协程支持,但理解如何从零实现一个简单的协程库,有助于深入掌握其底层机制。下面介绍基于 C++11+ 的手写协程实现原理,并给出一个简易示例。

协程的基本概念

协程允许函数执行到某一点时暂停,保存当前状态,之后从中断处继续执行。它不同于线程,不依赖操作系统调度,开销小,适合高并发场景。

一个最简协程需要具备:

  • 可挂起(suspend)和恢复(resume)的能力
  • 上下文切换(context switch)机制
  • 状态管理(如是否完成、返回值等)

使用 ucontext 实现上下文切换

注意:ucontext 是 POSIX 标准的一部分,在 macOS 和 linux 上可用,windows 不支持。

我们可以借助 getcontextsetcontextmakecontextswapcontext 来实现协程的跳转。

立即学习C++免费学习笔记(深入)”;

核心思路是:

  • 每个协程拥有独立的空间和上下文(ucontext_t)
  • 主函数启动协程时,通过 makecontext 创建执行环境
  • 协程运行中可通过 swapcontext 切回主函数或其他协程

简易协程库实现示例

下面是一个极简的协程实现,包含创建、切换与恢复功能:

c++怎么实现一个简单的协程库_c++手写协程实现原理与示例

豆包AI编程

豆包推出的ai编程助手

c++怎么实现一个简单的协程库_c++手写协程实现原理与示例 483

查看详情 c++怎么实现一个简单的协程库_c++手写协程实现原理与示例

#include <iostream> #include <ucontext.h> #include <map> #include <functional> #include <cstdlib>  class SimpleCoroutine { private:     static std::map<int, ucontext_t> coroutines;     static int current_id;     static ucontext_t main_context;      ucontext_t ctx;     char* stack;     int id;     bool is_done;  public:     SimpleCoroutine(std::function<void()> func) {         id = ++current_id;         stack = new char[8192]; // 8KB 栈空间         is_done = false;          getcontext(&ctx);         ctx.uc_stack.ss_sp = stack;         ctx.uc_stack.ss_size = 8192;         ctx.uc_link = &main_context; // 协程结束回到主上下文         makecontext(&ctx, (void(*)())lambda_wrapper, 1, this);          coroutines[id] = ctx;     }      ~SimpleCoroutine() {         delete[] stack;     }      static void lambda_wrapper(SimpleCoroutine* self) {         self->is_done = false;         self->run();         self->is_done = true;     }      void run() {         // 模拟协程体         std::cout << "协程 " << id << " 开始执行n";         yield(); // 第一次让出         std::cout << "协程 " << id << " 继续执行n";         yield();         std::cout << "协程 " << id << " 执行结束n";     }      void resume() {         if (is_done) return;         swapcontext(&main_context, &coroutines[id]);     }      static void yield() {         swapcontext(&coroutines[current_id], &main_context);     }      static void yield(int cid) {         swapcontext(&coroutines[current_id], &main_context);     } };  // 静态成员定义 std::map<int, ucontext_t> SimpleCoroutine::coroutines; int SimpleCoroutine::current_id = 0; ucontext_t SimpleCoroutine::main_context;

使用示例

测试两个协程交替执行:

int main() {     getcontext(&SimpleCoroutine::main_context);      SimpleCoroutine co1([]{}); // lambda 只是为了占位,实际逻辑在 run 中     SimpleCoroutine co2([]{});      std::cout << "主函数启动协程1n";     co1.resume();      std::cout << "主函数启动协程2n";     co2.resume();      std::cout << "主函数再次恢复协程1n";     co1.resume();      std::cout << "主函数再次恢复协程2n";     co2.resume();      return 0; }

输出结果大致为:

主函数启动协程1 协程 1 开始执行 主函数启动协程2 协程 2 开始执行 主函数再次恢复协程1 协程 1 继续执行 主函数再次恢复协程2 协程 2 继续执行

原理总结

这个简易协程库的核心在于:

  • 每个协程有自己的栈和上下文,通过 ucontext 系列函数管理
  • makecontext 将函数绑定到指定上下文和栈上
  • swapcontext 实现两个上下文之间的切换
  • 协程结束后自动返回主上下文(uc_link 设置)

虽然这只是一个玩具级实现,但它展示了协程的本质:控制流的主动让出与恢复。

基本上就这些。真实生产环境中的协程库(如 Boost.Context、libco)会处理更多细节:异常安全、内存对齐、跨平台兼容、调度器等。但对于理解原理,这个例子足够清晰。

text=ZqhQzanResources