gevent是基于协程的Python网络库。linux
协程存在的意义:对于多线程应用,CPU经过切片的方式来切换线程间的执行,线程切换时须要耗时(保存状态,下次继续)。协程,则只使用一个线程,在一个线程中规定某个代码块执行顺序,当程序中存在大量不须要CPU的操做时(IO),适用于协程。编程
特色:基于libev的快速事件循环(Linux上epoll,FreeBSD上kqueue),基于greenlet的轻量级执行单元。并且其中有个monkey类,将现有基于Python线程直接转化为greenlet(相似于打patch)。windows
libev:libev是libevent以后的一个事件驱动的编程框架,其接口和libevent基本相似。据官方介绍,其性能比libevent还要高,bug比libevent还少。Libev经过一个structev_loop结构表示一个事件驱动的框架。在这个框架里面经过ev_xxx结构,ev_init、ev_xxx_set、ev_xxx_start接口向这个事件驱动的框架里面注册事件监控器,当相应的事件监控器的事件出现时,便会触发该事件监控器的处理逻辑,去处理该事件。处理完以后,这些监控器进入到下一轮的监控中。而libevent是一个事件触发的网络库,适用于windows、linux、bsd等多种平台,内部使用select、epoll、kqueue、IOCP等系统调用管理事件机制,libevent支持用户使用三种类型的事件,分别是网络IO、定时器、信号三种,Libev 除了提供了基本的三大类事件(IO事件、定时器事件、信号事件)外还提供了周期事件、子进程事件、文件状态改变事件等多个事件,libevent支持多线程编程,每一个事件须要关联到本身的event_base。网络
greenlet:指的是使用一个任务调度器和一些生成器或者协程实现协做式用户空间多线程的一种伪并发机制,即所谓的微线程。主要思想是:生成器函数或者协程函数中的yield语句挂起函数的执行,直到稍后使用next()或send()操做进行恢复为止。可使用一个调度器循环在一组生成器函数之间协做多个任务。greenlet不是一种真正的并发机制,而是在同一线程内,在不一样函数的执行代码块之间切换,实施“你运行一会、我运行一会”,而且在进行切换时必须指定什么时候切换以及切换到哪,所以,greenlet本质是一种合理安排了的串行。多线程
monkey patch:在最开头的地方gevent.monkey.patch_all();把标准库中的thread/socket等给替换掉.这样咱们在后面使用socket的时候能够跟日常同样使用,无需修改任何代码,若是不进行 monkey patch,会形成严重后果,性能降低,数据错误,代码切换异常等。并发
要想理解gevent首先要理解gevent的调度流程,gevent中有一个hub的概念,也就是MainThread,用于调度全部其它的greenlet实例。框架
每次从hub切换到一个greenlet后,都会回到hub,这就是gevent的关键,gevent中并无greenlet链的说法,全部都是向主循环注册greenlet.switch方法,主循环在合适的时机切换回来。为何每次都要切换到hub呢?socket
1.hub是事件驱动的核心,每次切换到hub后将继续循环事件。若是在一个greenlet中不出来,那么其它greenlet将得不到调用。函数
2.维持二者关系确定比维持多个关系简单。每次咱们所关心的就是hub以及当前greenlet,不须要考虑各个greenlet之间关系。oop
下面看一个简单的gevent的例子:
咱们对这段小代码进行debug,而后在控制台查看线程数:
只有一个线程,他run的实际上是greenlet这个伪线程。
而后看执行结果:
咱们用显式的sleep使其进行切换,能够看到在sleep后,代码切换到了其余greenlet,由于gevent认为此处出现了阻塞,一旦检测到阻塞,gevent就会自动进行greenlet切换。若是不进行显式调用,则greenlet实际上是顺序执行的,由于本质上它是串行的,如今咱们将sleep去掉,结果是这样:
实际代码里,咱们不会用gevent.sleep()去切换协程,而是在执行到IO操做时,gevent自动切换,代码以下:
咱们没有显式的进行sleep,而是依赖各个IO操做自身的阻塞时间,下面咱们看下结果:
能够看到,结束顺序和IO发起顺序并不一致。gevent.spawn()方法建立greenlet实例并调用其start方法发起,而后经过gevent.joinall将greenlet实例加入到greenlet执行队列中等待其完成,这里能够为其设置超时时间。
gevent还能够做为起celery worker和celery beat时的POOL(支持prefork (default), eventlet, gevent, solo or threads),在启动celery worker时,-P为gevent,即指定gevent为POOL。