协程(Coroutine
)
什么是协程,协程又被称为微线程,那么协程是作用在于执行函数 A 的时候, 可以随时中断,然后执行函数 B ,然后又可以中断函数 B 去执行函数 A,也就是说可以进行自由来回切换,但是这个一个过程并不是调用函数,我们要注意一个点,就是这个过程看起来是多线程,但是协程是位于线程里面的,是单线程的
在了解协程之前,先来看看生成器(generator
),很多人一听到生成器,第一反应可能就是 yield
,那么先用一些案例来看下
1 | import time |
通过这个案例可以,可以清楚的知道使用生成器的时间比列表返回快,因为如果使用列表,那么每次返回的时候,必须计算好整个列表的值,然后返回,那么就需要一个比较大的空间用来保存,但是生成器不需要
1 | # yield send 通过yield暂停函数 然后返回数据 如果向函数内部发送数据 |
1 | def consumer(): |
greenlet 和 gevent 的使用
greenlet的案例
http://www.bjhee.com/greenlet.html1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27from greenlet import greenlet
import random
def test1():
print(1)
gr2.switch()
print(2)
# gr2.switch() # 如果这个不写 就不能打印4
def test2():
print(3)
gr1.switch()
print(4)
gr1 = greenlet(test1)
gr2 = greenlet(test2)
gr1.switch() # 开始运行 水调用了switch 就会切换到哪里去
"""
这里创建了两个greenlet协程对象,gr1和gr2,分别对应于函数test1()和test2()。
使用greenlet对象的switch()方法,即可以切换协程。上例中,我们先调用”gr1.switch()”,函数test1()被执行,
然后打印出”1″;接着由于”gr2.switch()”被调用,协程切换到函数test2(),打印出”3″;之后”gr1.switch()”又被调用,
所以又切换到函数test1()。但注意,由于之前test1()已经执行到第5行,也就是”gr2.switch()”,所以切换回来后会继续往下执行,
也就是打印”2″;现在函数test1()退出,同时程序退出。由于再没有”gr2.switch()”来切换至函数test2(),所以程序”print 4″不会被执行。
switch带参数的版本
1 | from greenlet import greenlet |
gevent
有了gevent,协程的使用将无比简单,你根本无须像greenlet一样显式的切换,每当一个协程阻塞时,程序将自动调度,gevent处理了所有的底层细节
1 | import gevent |
“””
解释下,”gevent.spawn()”方法会创建一个新的greenlet协程对象,
并运行它。”gevent.joinall()”方法会等待所有传入的greenlet协程运行结束后再退出,
这个方法可以接受一个”timeout”参数来设置超时时间,单位是秒。运行上面的程序,
执行顺序如下:
先进入协程test1,打印1
遇到”gevent.sleep(0)”时,test1被阻塞,自动切换到协程test2,打印3
之后test2被阻塞,这时test1阻塞已结束,自动切换回test1,打印2
当test1运行完毕返回后,此时test2阻塞已结束,再自动切换回test2,打印78
所有协程执行完毕,程序退出
“””
greenlet一个协程运行完后,必须显式切换,不然会返回其父协程。而在gevent中,一个协程运行完后,它会自动调度那些未完成的协程
gevent生产者和消费者1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23from gevent.queue import Queue
import gevent
queue = Queue(3)
def producer():
while True:
item = random.randint(0, 99)
queue.put(item)
print('生产者 生产了 {}'.format(item))
gevent.sleep(2)
def consumer():
while True:
item = queue.get()
print('消费者 消费了 {}'.format(item))
gevent.sleep(2) # 模拟阻塞
p = gevent.spawn(producer) # spawn启动协程 第二个是参数
c = gevent.spawn(consumer)
gevent.joinall([p, c]) # 阻塞当前流程 执行给定的 greenlet 执行完了 在继续
并发服务器
1 | from gevent import monkey; monkey.patch_socket() |
仅用于个人参考