Python
通过yield
提供了对协程的基本支持,但是不完全。而第三方的gevent
为Python提供了比较完善的协程支持。
gevent是第三方库,通过greenlet实现协程,其基本思想是:
当一个greenlet遇到IO操作时,比如访问网络,就自动切换到其他的greenlet,等到IO操作完成,再在适当的时候切换回来继续执行。由于IO操作非常耗时,经常使程序处于等待状态,有了gevent为我们自动切换协程,就保证总有greenlet在运行,而不是等待IO
生成器send方式:
def create_num(all_num):
a,b = 0,1
current_num = 0
while current_num < all_num:
ret = yield a
print(ret)
a,b = b, a+b
current_num +=1
obj = create_num(10)
ret= next(obj)
print(ret)
ret = obj.send('鲁班七号')
print(ret)
>>>
0
鲁班七号
1
yield完成多任务
import time
def task1():
while True:
print("task one")
time.sleep(1)
yield
def task2():
while True:
print("task 2")
time.sleep(1)
yield
def main():
obj1 = task1()
obj2 = task2()
while True:
next(obj1)
next(obj2)
if __name__ == '__main__':
main()
>>>
task one
task 2
task one
task 2
task one
......
使用greenlet完成多任务
from greenlet import greenlet
import time
def f1():
while True:
print('-----A-----')
gr2.switch()
time.sleep(0.5)
def f2():
while True:
print('-----B-----')
gr1.switch()
time.sleep(0.5)
if __name__ == '__main__':
gr1 = greenlet(f1)
gr2 = greenlet(f2)
gr1.switch()
gevent
import gevent
import time
def f1(n):
for i in range(n):
print(gevent.getcurrent(),i)
gevent.sleep(1) #模拟一个耗时操作,模拟耗时操作要用gevent里面函数
def f2(n):
for i in range(n):
print(gevent.getcurrent(),i)
gevent.sleep(1)
if __name__ == '__main__':
g1 = gevent.spawn(f1,5)
g2 = gevent.spawn(f2,5)
g1.join()
g2.join()
>>>
<Greenlet at 0x2f598b0: f1(3)> 0
<Greenlet at 0x2f599c0: f2(3)> 0
<Greenlet at 0x2f598b0: f1(3)> 1
<Greenlet at 0x2f599c0: f2(3)> 1
<Greenlet at 0x2f598b0: f1(3)> 2
<Greenlet at 0x2f599c0: f2(3)> 2
打补丁:不替换sleep, socket.conn,socket.reciv...
等
由于切换是在IO操作时自动完成,所以gevent需要修改Python自带的一些标准库,这一过程在启动时通过monkey patch完成
from gevent import monkey
#打补丁,保证不用替换原来耗时操作的函数
monkey.patch_all()
join_all方法:
gevent.joinall([
gevent.spawn(f1, 5),
gevent.spawn(f2, 5)
])
#encoding:utf-8
# __author__ = 'donghao'
# __time__ = 2019/4/2 13:57
import gevent
import time
from gevent import monkey
#打补丁,保证不用替换原来耗时操作的函数
monkey.patch_all()
def f1(n):
for i in range(n):
print(gevent.getcurrent(),i)
time.sleep(1)
def f2(n):
for i in range(n):
print(gevent.getcurrent(),i)
time.sleep(1)
if __name__ == '__main__':
gevent.joinall([
gevent.spawn(f1, 5),
gevent.spawn(f2, 5)
])
# 相当于:
# g1 = gevent.spawn(f1,5)
# g2 = gevent.spawn(f2,5)
# g1.join()
# g2.join()
print('end of main')
>>>
<Greenlet at 0x39fc2d8: f1(5)> 0
<Greenlet at 0x39fc360: f2(5)> 0
<Greenlet at 0x39fc2d8: f1(5)> 1
<Greenlet at 0x39fc360: f2(5)> 1
<Greenlet at 0x39fc2d8: f1(5)> 2
<Greenlet at 0x39fc360: f2(5)> 2
<Greenlet at 0x39fc2d8: f1(5)> 3
<Greenlet at 0x39fc360: f2(5)> 3
<Greenlet at 0x39fc2d8: f1(5)> 4
<Greenlet at 0x39fc360: f2(5)> 4
end of main
由于gevent是基于IO切换的协程,所以最神奇的是,我们编写的Web App
代码,不需要引入gevent的包,也不需要改任何代码,仅仅在部署的时候,用一个支持gevent
的WSGI
服务器,立刻就获得了数倍的性能提升