使用context实现的C++协程库

mac2024-04-21  39

先上大佬的github,这个协程库的实现主要参考大佬的思路,所做的工作只是将代码做一个面向对象的封装。

协程是什么?

协程其实可以理解成一种更细粒度的线程,不同的协程在一个线程中执行不同的代码序列,但是不需要进行线程的切换,也有人把这种实体叫做用用户级线程。其他的语言例如Python和Golang中都有比较完善的对协程的实现。要实现协程主要是要能保存和恢复上下文。

运用context保存协程上下文

在linux和MacOS的早起版本上都提供了context相关的API,这些API是用来实现协程库的一个关键,这里简单介绍一下。

ucontext数据类型

这个是用来保存线程上下文的主要数据结构,在Unix的手册(man)中有如下介绍:

The ucontext_t structure contains at least these fields: ucontext_t *uc_link context to assume when this one returns sigset_t uc_sigmask signals being blocked stack_t uc_stack stack area mcontext_t uc_mcontext saved registers

uc_link主要指向的是当前上下文返回之后指向的上下文uc_mcontext主要是保存一些寄存器uc_sigmask主要保存阻塞信号量集(apue中有介绍)uc_stack主要是保存堆栈信息

getcontext

函数原型:

int getcontext(ucontext_t *ucp);

这个函数主要是将当前进程的上下文保存到ucp指向的数据结构里,为了后续的setcontext 调用

makecontext

函数原型:

void makecontext(ucontext_t *ucp, void (*func)(), int argc, ...)

这个函数主要是用以自己构造上下文,主要是需要引入自己的函数时调用(作用可能和pthread_create比较类似)。手册上说ucp所指向的结构之前必须需要被getcontext初始化,并且需要已经分配的栈空间。

setcontext

函数原型:

setcontext(const ucontext_t *ucp);

这个函数主要是用来保存当前进程的context。 另外需要注意的是

如果ucp是由getcontext得到的话,那么setcontext的执行序列就如同调用getcontext之前一样。如果ucp是由makecontext得到的话,那么当这个函数返回时,如果ucp指向的uc_link为空的话,进程退出;否则的话就直接指向ucp指向的上下文

swapconext

函数原型:

int swapcontext(ucontext_t *oucp, const ucontext_t *ucp);

这个函数就比较简单了,相当于是先调用getcontext到oucp,然后调用setconext将ucp所指向的上下文进行装载

有了以上的API,我们就可以使用context来记录各个协程的上下文

coroutine

实现的coroutine主要有两个结构:

schedule

主要是一个管理单元

class schedule { private: static const int DEFAULT_COROUTINE = 16; //默认的协程数目 char stack[STACK_SIZE]; //堆上空间,用来作为当前协程的执行栈 ucontext_t main; //当前协程的上下文 int nco; //当前的协程数目 int cap; //当前的协程容量 int running; //当前运行的协程编号 coroutine **co; //协程列表 };

coroutine

这个结构用来保存协程相关的信息

class coroutine { private: coroutine_func func; //执行函数 void *ud; //执行函数的参数 ucontext_t ctx; //上下文 struct schedule *sch; //指向schedule ptrdiff_t cap; ptrdiff_t size; CoroutineState status; //当前协程状态 char *stack; //当前协程的栈 friend class schedule; };

之前大佬的代码都写成了全局的函数,我都修改为了类的成员函数。

上github:https://github.com/Cherryfla/Cpp_coroutine

测试

在1核1G的平台上每秒约能进行7e5次协程切换 不知道为什么在这个平台上只能支持257个协程,可能是由于保存的栈占的空间太大了

只想到这些,想到其他的以后再更新。

最新回复(0)