协程回顾

在这里插入图片描述
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的包,也不需要改任何代码,仅仅在部署的时候,用一个支持geventWSGI服务器,立刻就获得了数倍的性能提升

posted @ 2019-04-02 14:46  最美的烟火  阅读(223)  评论(0编辑  收藏  举报