协程:Greenlet模块、Gevent模块

三、Greenlet模块                                                            

Greenlet是python的一个C扩展,来源于Stackless python,旨在提供可自行调度的‘微线程’, 即协程。generator实现的协程在yield value时只能将value返回给调用者(caller)。 而在greenlet中,target.switch(value)可以切换到指定的协程(target), 然后yield value。greenlet用switch来表示协程的切换,从一个协程切换到另一个协程需要显式指定。


安装 :pip3 install greenlet
简介:
from greenlet import greenlet

def eat(name):
    print('%s eat 1' %name)
    g2.switch('egon')
    print('%s eat 2' %name)
    g2.switch()
def play(name):
    print('%s play 1' %name)
    g1.switch()
    print('%s play 2' %name)

g1=greenlet(eat)
g2=greenlet(play)

g1.switch('egon')#可以在第一次switch时传入参数,以后都不需要
greenlet实现状态切换

有几个缺点
1.手动切换
2.不能规避I/O操作(睡眠)

gevent模块 

安装:pip3 install gevent

Gevent 是一个第三方库,可以轻松通过gevent实现并发同步或异步编程,在gevent中用到的主要模式是Greenlet, 它是以C扩展模块形式接入Python的轻量级协程。 Greenlet全部运行在主程序操作系统进程的内部,但它们被协作式地调度。
介绍、安装
g1=gevent.spawn(func,1,,2,3,x=4,y=5)创建一个协程对象g1,spawn括号内第一个参数是函数名,如eat,后面可以有多个参数,可以是位置实参或关键字实参,都是传给函数eat的

g2=gevent.spawn(func2)

g1.join() #等待g1结束

g2.join() #等待g2结束

#或者上述两步合作一步:gevent.joinall([g1,g2])

g1.value#拿到func1的返回值



#例:遇到io主动切换

from gevent import monkey;monkey.patch_all()

import gevent
import time
def eat():
    print('eat food 1')
    time.sleep(2)
    print('eat food 2')

def play():
    print('play 1')
    time.sleep(1)
    print('play 2')

g1=gevent.spawn(eat)
g2=gevent.spawn(play)
gevent.joinall([g1,g2])
print('')
用法介绍
真正能实现协程的模块gevent

import gevent
def eat():
    print('eating1')
    print('eating2')
g1 = gevent.spawn(eat)  #创建一个协程对象g1
#执行输出为空,表示它还没执行。


import gevent
def eat():
    print('eating1')
    print('eating2')
g1 = gevent.spawn(eat)  #创建一个协程对象g1
g1.join()  #等待g1结束

#执行输出:

#eating1
#eating2
例子

geven 不能识别time.sleep()  需要用gevent.sleep()  或者导入一个模块monkey;monkey patch (猴子补丁)

#如果想让协程执行time.sleep()呢?由于默认,协程无法识别time.sleep()方法,需要导入一个模块monkey

#monkey patch (猴子补丁)
#用来在运行时动态修改已有的代码,而不需要修改原始代码。

from gevent import monkey;monkey.patch_all()
# 它会把下面导入的所有的模块中的IO操作都打成一个包,gevent就能够认识这些IO了
import time
import gevent
def eat():
    print('eating1')
    time.sleep(1)  #延时调用
    print('eating2')
 
def play():
    print('playing1')
    time.sleep(1)  #延时调用
    print('playing2')
 
g1 = gevent.spawn(eat)  #创建一个协程对象g1
g2 = gevent.spawn(play)
g1.join()  #等待g1结束
g2.join()

#执行输出:

eating1
playing1
eating2
playing2
使用

结论:

使用gevent模块来执行多个函数,表示在这些函数遇到IO操作的时候可以在同一个线程中进行切换
利用其他任务的IO阻塞时间来切换到其他的任务继续执行

前提是:

spawn来发布协程任务
join负责开启并等待任务执行结束
gevent本身不认识其他模块中的IO操作,但是如果我们在导入其他模块之前执行from gevent import monkey;monkey.patch_all()  这行代码,必须在文件最开头
gevent就能够认识在这句话之后导入的模块中的所有IO操作了

Gevent之同步与异步

from gevent import spawn,joinall,monkey;monkey.patch_all()
 
import time
def task(pid):
    """
    Some non-deterministic task
    """
    time.sleep(0.5)
    print('Task %s done' % pid)
 
 
def synchronous():  # 同步
    for i in range(10):
        task(i)
 
def asynchronous(): # 异步
    g_l=[spawn(task,i) for i in range(10)]
    joinall(g_l)
    print('DONE')
     
if __name__ == '__main__':
    print('Synchronous:')
    synchronous()
    print('Asynchronous:')
    asynchronous()
#  上面程序的重要部分是将task函数封装到Greenlet内部线程的gevent.spawn。
#  初始化的greenlet列表存放在数组threads中,此数组被传给gevent.joinall 函数,
#  后者阻塞当前流程,并执行所有给定的greenlet任务。执行流程只会在 所有greenlet执行完后才会继续向下走。
实例

当一个任务执行时,依赖另外一个任务的结果时,这种情况不适合异步,只能用同步

Gevent之应用举例一

Gevent之应用举例二

猜你喜欢

转载自www.cnblogs.com/python-by-xiaoma/p/10300000.html