icoders 2017-09-17 1105浏览量
Gevent官网文档地址:http://www.gevent.org/contents.html
我们通常所说的协程Coroutine其实是corporate routine的缩写,直接翻译为协同的例程,一般我们都简称为协程。
在linux系统中,线程就是轻量级的进程,而我们通常也把协程称为轻量级的线程即微线程。
下面对比一下进程和协程的相同点和不同点:
相同点:
我们都可以把他们看做是一种执行流,执行流可以挂起,并且后面可以在你挂起的地方恢复执行,这实际上都可以看做是continuation,关于这个我们可以通过在linux上运行一个hello程序来理解:
shell进程和hello进程:
当我们挂起一个执行流的时,我们要保存的东西:
而寄存器和栈的结合就可以理解为上下文,上下文切换的理解:
CPU看上去像是在并发的执行多个进程,这是通过处理器在进程之间切换来实现的,操作系统实现这种交错执行的机制称为上下文切换
操作系统保持跟踪进程运行所需的所有状态信息。这种状态,就是上下文。
在任何一个时刻,操作系统都只能执行一个进程代码,当操作系统决定把控制权从当前进程转移到某个新进程时,就会进行上下文切换,即保存当前进程的上下文,恢复新进程的上下文,然后将控制权传递到新进程,新进程就会从它上次停止的地方开始。
不同点:
既然我们上面也说了,协程也被称为微线程,下面对比一下协程和线程:
我们通过下面的图更容易理解:
从上图可以看出,协程只是在单一的线程里不同的协程之间切换,其实和线程很像,线程是在一个进程下,不同的线程之间做切换,这也可能是协程称为微线程的原因吧
继续分析协程:
Gevent是一种基于协程的Python网络库,它用到Greenlet提供的,封装了libevent事件循环的高层同步API。它让开发者在不改变编程习惯的同时,用同步的方式写异步I/O的代码。
使用Gevent的性能确实要比用传统的线程高,甚至高很多。但这里不得不说它的一个坑:
既然Gevent用的是Greenlet,我们通过下图来理解greenlet:
每个协程都有一个parent,最顶层的协程就是man thread或者是当前的线程,每个协程遇到IO的时候就把控制权交给最顶层的协程,它会看那个协程的IO event已经完成,就将控制权给它。
下面是greenlet一个例子
1 from greenlet import greenlet 2 3 def test1(x,y): 4 z = gr2.switch(x+y) 5 print(z) 6 7 8 def test2(u): 9 print(u) 10 gr1.switch(42) 11 12 13 gr1 = greenlet(test1) 14 gr2 = greenlet(test2) 15 16 17 gr1.switch("hello",'world')
greenlet(run=None, parent=None): 创建一个greenlet实例.
gr.parent:每一个协程都有一个父协程,当前协程结束后会回到父协程中执行,该 属性默认是创建该协程的协程.
gr.run: 该属性是协程实际运行的代码. run方法结束了,那么该协程也就结束了.
gr.switch(*args, **kwargs): 切换到gr协程.
gr.throw(): 切换到gr协程,接着抛出一个异常.
下面是gevent的一个例子:
1 import gevent 2 3 def func1(): 4 print("start func1") 5 gevent.sleep(1) 6 print("end func1") 7 8 9 def func2(): 10 print("start func2") 11 gevent.sleep(1) 12 print("end func2") 13 14 gevent.joinall( 15 [ 16 gevent.spawn(func1), 17 gevent.spawn(func2) 18 ] 19 )
gevent中也有自己的队列,但是有一个场景我用的过程中发现一个问题,就是如果我在协程中通过这个q来传递数据,如果对了是空的时候,从队列获取数据的那个协程就会被切换到另外一个协程中,这个协程用于往队列里put放入数据,问题就出在,gevent不认为这个放入数据为IO操作,并不会切换到上一个协程中,会把这个协程的任务完成后在切换到另外一个协程。我原本想要实现的效果是往对了放入数据后就会切换到get的那个协程。(或许我这里理解有问题)下面是测试代码:
1 import gevent 2 from gevent.queue import Queue 3 4 5 def func(): 6 for i in range(10): 7 8 print("int the func") 9 q.put("test") 10 11 def func2(): 12 for i in range(10): 13 print("int the func2") 14 res = q.get() 15 print("--->",res) 16 17 q = Queue() 18 gevent.joinall( 19 [ 20 gevent.spawn(func2), 21 gevent.spawn(func), 22 ] 23 )
这段代码的运行效果为:
如果我在fun函数的q.put("test")后面添加gevent.sleep(0),就会是如下效果:
原本我预测的在不修改代码的情况下就应该是第二个图的结果,但是实际却是第一个图的结果(这个问题可能是我自己没研究明白,后面继续研究)
就像我上面说的gevent和第三方库配合使用会有一些问题,可以总结为:
python协程的库可以直接monkey path
C写成的库可以采用豆瓣开源的greenify来打patch(这个功能自己准备后面做测试)
不过总的来说gevent目前为止还是有很多缺陷,并且不是官网标准库,而在python3中有一个官网正在做并且在3.6中已经稳定的库asyncio,这也是一个非常具有野心的库,非常建议学习,我也准备后面深入了解
所有的努力都值得期许,每一份梦想都应该灌溉!版权声明:本文内容由阿里云实名注册用户自发贡献,版权归原作者所有,阿里云开发者社区不拥有其著作权,亦不承担相应法律责任。具体规则请查看《阿里云开发者社区用户服务协议》和《阿里云开发者社区知识产权保护指引》。如果您发现本社区中有涉嫌抄袭的内容,填写侵权投诉表单进行举报,一经查实,本社区将立刻删除涉嫌侵权内容。
集结各类场景实战经验,助你开发运维畅行无忧