协程原理与实现-高并发低内存

1 协程的运用场景

在网络高并发的场景下,epoll作为解决方案的首选,基本可以达到C1000K甚至更好的并发量,但是由于需要依靠线程进行异步的回调操作,在一定意义上资源的开销并没有降低,另一方面异步操作带来的问题就是整体复杂度大大提高。协程就是为了解决上述的问题,同步的编程方式,达到异步的性能,最重要的是资源的开销更是小于多线程。

3 协程原理

协程包括几大部分组成,一个是协程本身,第二个是协程的调度器,第三是协程的切换。整体过程就是当协程创建完成之后,将协程加入到调度器统一进行管理,执行协程是由统一的调度算法来决定,协程具体操作是遇到IO操作之前调用yeild让出执行权,然后由调度器决定下一个换入resume操作,加载下一个执行的协程,执行完成后将回到yeild位置继续执行IO操作。整体给人的感觉就是整个调度器将时间划分为不同的时间片,并且交由不同协程去完成相关的操作。没有操作时,主动权将移交给调度器统一控制。
在这里插入图片描述

3.1 协程的切换

上面可以看到协程两个重要的原语操作,一个是yeild主要负责将当前的操作保存,切换到上一次resume位置继续执行,另一个是resume,将当前的操作保存,回到上一次yeild操作。两个原语操作是相互可逆的操作,实现了从调度器切换到协程A运行,然后由协程A切换回调度器,执行协程B,协程B的执行完成,再次回到协程A继续执行,如此反复,保证调度器有任务处理的时候始终处于任务处理状态,而不会出现等待的情况。
如何实现这一技术,主要是从CPU工作的原理出发,当程序执行时,主要是利用寄存器进行数据计算,然后利用栈进行数据的临时数据的保存。为了做到协程A和协程B之间的相互隔离,需要保护的现场包括,运算寄存器的值和,堆栈的信息,两部分组成。

3.2 协程的定义

协程的定义涉及到多方面的定义,一个是上下文的定义,上下文主要是包括寄存器状态和堆栈的信息状态。

//寄存器,栈信息
typedef struct _haw_reg_ctx {
	void *reg[16];//寄存器信息
	void *stack; //栈指针
	size_t stack_size;//栈大小
}haw_reg_ctx;

第二部分是一个协程的定义,协程主要包括几大部分,首先是协程自身的一个ID用于标识协程;第二个是协程的状态,目前初步规划是就绪状态、等待状态、睡眠状态和结束状态四种;第三协程需要一个上下文,用于保存协程的运行时状态主要是方便恢复;第四部分是协程程序体,包括协程的参数;第五部分是协程的调度器,保存调度器的指针;第六部分是预留的节点,用于各个状态之间的切换用。
这里引出了一个问题就是就绪队列,睡眠队列,等待队列到底用什么数据结构保存。首先是就绪队列,这里采用的是队列的方式,主要是考虑到协程的运行并没有优先级,采用先进先出的方式,保证协程可以被很好的执行到。至于就绪队列和等待队列考虑到用红黑树,主要是为了取数据方便,根据睡眠或者等待的时间进行中序遍历,可以方便的取出其中小于某个时间的多个协程节点。

typedef enum _haw_co_status {
	HAW_CO_READY,
	HAW_CO_SLEEP,
	HAW_CO_WAIT,
	HAW_CO_DEAD,
}haw_co_status;
//协程
typedef struct _haw_coroutine {
	uint64_t id; //协程ID
	uint64_t time; //创建时间
	haw_co_status status; //协程状态
	
	haw_reg_ctx *ctx; //协程的上下文
	void *func;//协程执行体
	void *arg;//传递参数
	
	haw_co_schedule *sch;//调度器

	queue(haw_coroutine) ready_node;//就绪队列节点
	rbtree(haw_coroutine) sleep_node;//睡眠红黑树节点
	rbtree(haw_coroutine) wait_node; //等待红黑树节点
}haw_coroutine ;

3.3 协程的调度器

调度器整体分为三个部分,一个是当前协程的一些信息,另一个是epoll事件管理;第三部分是协程队列用于保存所有协程的信息。

//调度器
typedef struct _haw_co_schedule {
		//当前协程信息
		uint64_t time;
		haw_reg_ctx *ctx;
		haw_coroutine *cur_co;

		//epoll io事件管理
		int epfd;
		int socketfd;
		struct epoll_event *events;
		int events;
		
		haw_co_queue ready;//就绪队列
		haw_co_rbtree sleep;//睡眠红黑树
		haw_co_rbtree wait;//等待红黑树
}haw_co_schedule ;

3.4 协程的调度策略

调度策略这里采用的是平行的调度策略,谁有时间就行相关处理,对于睡眠队列,当超时时间到后即进行恢复运行权;等待队列是当接收到epoll触发事件后,进行运行权利的恢复;对于就行队列,只有存在元素即可恢复程序的运行权。
在这里插入图片描述

//核心调度逻辑
while(1)
{
	//遍历所有睡眠集合,使用resume恢复到期的协程运行权
	haw_coroutine *exp_co = NULL;
	while((exp_co = sleep_rbtree_expired(schedule)) != NULL)
	{
		resume(exp_co);
	}
	
	//遍历所有等待集合,使用resume恢复等待协程的运行权
	haw_coroutine *wait_co = NULL;
	int nready = epoll_wait(schedule->epfd, events, MAX_EPOLL, 1);
	for(i = 0; i < nready; i++)
	{
		wait_co = wait_rbtree_search(events[i].data.fd);
		resume(wait_co);
	}
	
	//遍历所有的就绪队列,使用resume恢复就绪协程的运行权
	haw_coroutine *ready_co = NULL;
	while(!queue_empty(schedule->ready))
	{
		ready_co = queue_pop(schedule->ready);
		resume(ready_co);
	}
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值