拥抱新技术 —— 协程

写在前面
本文主要针对使用C/C++或有C/C++基础的读者,讨论Tencent的开源网络库libco

什么是协程

很多人描述协程为 “轻量级线程”“用户态线程” 等等。之前博主对这个东西也很迷惑?

这都是啥!!!不急,博主将会写一系列文章来理清楚协程,此为系列文章的第一篇,主要介绍协程的一些基本知识:

为了理清楚协程,我们还得对线程的概念进行回忆一下:

shenmingik
线程是操作系统能够进行运算调度的最小单位。被包含在进程之中,是进程的实际运作单位。一条线程指的是进程中一个单一顺序的控制流,一个进程可以并发执行多个线程,每个线程会执行不同的任务。对应在现实生活中,进程是组长,线程是小组成员。

既然说线程是系统调度的最小单位,那么又何来说协程的概念呢?通过阅读大量的文献以及google之后,我对协程有了自己的看法。

  • 一个线程中可以由多个协程,协程是运行在线程之中的逻辑处理单元
  • 协程在线程之间的运行是串行的
  • 协程有自己的寄存器和栈
  • 协程之间的调度通过调度器或者自己主动放开对CPU的占有,让给其他协程
  • 协程调度只存在于用户态

下面我将一条一条来解读这些特性:

协程之于线程

刨除操作系统CPU对线程调度的因素,我们就单考虑一个线程之中的情况。

首先,协程之于线程跟线程之于进程是一样的,一个线程之中是有多个协程的
在这里插入图片描述
可以把线程想象成一个单核CPU,而协程A、B都是原来的进程。协程A、B通过一个叫协程调度器的东西来负责协程A、B的交替执行。


以上情况只涉及libco 中的设计,go语言中的协程其可以运行在多个“CPU”(线程)。

在libco中,一个协程只能运行在一个线程中,这也就是说它为什么在线程中是串行的原因。因为对于一个单核CPU中,也没法进行两个进程的并行啊。

协程的切换

切成的切换得益于其在用户态就保存了寄存器信息。这里面有当前协程的状态信息,有下一条执行的地址信息。这么说有点抽象,我们看一下下面的一个例子吧:

假设协程A正在执行以下函数:

void funa(void)
{
	int a = 10;
	int b = 20;
	sum(a,b);
}

如果说当协程A执行到sum函数的时候,协程调度器调度协程B,协程A的信息就会被保存起来。那么当前协程的的运行状态信息就是 a=10,b=20,下一条该执行的函数是sum。等待下次协程A被调度器调度,这些信息就会被恢复,再次从协程A去执行。

非对称协程和对称协程

协程目前分两种,一种是go语言采用的对称协程;一种是libco采用的非对称协程

这里借用知乎博主tx征服者的图来向大家说明:
协程学习(对称和非对称)
对称协程其实就是由协程调度器来负责,协程不允许调度其他协程。调度器resume协程A,那么协程A会yeild回调度器,再由调度器去执行其他的协程。如果我们把resume的虚线都放在左边,yeild的实线都放在右边。以调度器为中心,那么他是不是就是一个对称的图形呢?

我们再来看一下非对称协程
在这里插入图片描述
非对称调度由调度器来调度协程A,然后协程A再调动协程B。那么Byeild让出CPU使用权就不是让给调度器了,而是协程A。简而言之就是从哪儿来,回哪儿去。

同样,将此展开也不是一个对称的图形了。

独占栈和共享栈

独占栈(separate coroutine stacks)即为每个协程分配一个单独的、固定大小的栈
共享栈(copying the stack)则仅为当前正在运行的协程分配栈内存

共享栈这里其实就是当协程被切出去时,就把它实际占用的栈内存copy保存到一个单独分配的缓冲区;当被切出去的协程再次调度执行时,再一次copy将原来保存的栈内存恢复到那个共享的、固定大小的栈空间。

libco将两者都实现了,默认使用前者。

定时器

为什么要介绍定时器呢?是因为定时器是协程调度中不可缺失的一个部分。就跟CPU的时间片轮转法一样,协程调度器通过定时器来调度协程。

定时器是事件驱动模型的网络框架的必不可少的功能。网络I/O的超时、定时任务,包括定时等待都依赖于此。

一般而言,使用定时功能时,我们首先向定时器中注册一个定时事件,在注册定时事件时需要指定这个事件在未来的触发事件。在到了触发时间点后,我们会收到定时器的通知

网络中的定时器,一般可以看做两部分组成:

  • 第一部分是保存已注册的数据结构
  • 第二部分则是定时通知机制

libco中则是直接使用epoll来定时,其中的定时器实现算法如下:

  1. epoll_wait:调用epoll_wait等待I/O就绪事件,最大等待时长设置为1毫秒
  2. 处理I/O就绪事件:循环处理epoll_wait得到的I/O就绪文件描述符
  3. 从时间轮取超时事件:从时间轮取超时事件,放到timeout队列
  4. 处理超时事件:如果取到的超时事件不为空,那么就循环处理timeout队列中的定时任务
  5. 跳到步骤1

参考文献

[1] C++开源协程库libco详解
[2]协程学习(对称和非对称).tx征服者.知乎
评论 11
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

shenmingik

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值