一、协程(coroutine)又称微线程,纤程,是种用户级别的轻量级线程。
协程拥有自己的寄存器上下文和栈。协程调度切换时候,将寄存器上下文和栈保存到其他地方,等待切换回来的时候恢复,并从之前保存的寄存器上下文 和 栈继续工作。
并发编程中,协程与 线程类似,每个协程表示一个执行单元,有自己的本地数据,与其他协程共享全局数据及资源池。
协程需要操作员单独写调度逻辑,对CPU来说,协程也就是单线程,因此CPU 不需要考虑怎么调度、切换上下文,省去了CPU开销,所以,协程又在一定程度上好于多线程。
二、python中实现协程:
python使用yield 提供对协程的基本支持,但是,第三方的 gevent库能更好地提供该服务,gevent有比较完善的协程支持。
geve 是基于协程的python网络函数库,使用greenlet 在libev事件循环顶部提供一个有高级别并发性的API。
特点:
(1)基于libev 的快速事件循环,Linux上的epoll机制。
(2)基于greenlet的轻量级执行单元。
(3)API 复用了python标准库的内容。
(4)支持SSL的协作式sockets。
(5)可以通过线程池或者c-ares 实现DNS查询。
(6)通过 monkey patching功能使得第三方模块编程协作式。
gevent支持协程,其实也可以说是greenlet实现的工作切换。
greenlet工作流程如下:如果访问网路的I/O操作出现阻塞时,greenlet就显式切换到另外一个没有被阻塞的代码段执行,直到原先的阻塞状态消失后,再自动切换会原来的代码段继续处理。 可以说,greenlet是在更合理地安排串行工作方式。
同时,由于IO 操作比较耗时,经常是程序处于等待状态,gevent自动切换协程后,能够保证总有greenlet在运行,而不需要等待IO完成,这是协程比一般多线程效率高的原因。
IO操作是自动完成的,所以gevent 需要修改python的一些自带标准库,将一些常见的阻塞,如:socket、select等地方实现协程跳转,这一过程可以通过monkey patch完成。
如下代码可以显示 gevent的使用流程:(python版本: 3.6 操作系统环境: windows10)
gevent的spawn方法可以看做是用来形成协程,joinall 方法相当于添加协程任务并启动运行。由结果可以看到,3个网络请求并发执行,而且结束顺序却不一致,但是却只有一个线程。
gevent还提供池。如果拥有动态数量的 greenlet需要进行并发管理,可以使用池 来处理大量的网络请求 及 IO操作。
如下是 gevent的pool对象,修改如上的多网络请求示例:
由结果来看,Pool 对象对协程的并发数量进行了管理,先访问前两个,当其中一个任务完成了,再继续执行第三个请求。
领取专属 10元无门槛券
私享最新 技术干货