学习笔记(35):Python网络编程&并发编程-协程(yield,greenlet,gevent模块)

立即学习:https://edu.csdn.net/course/play/24458/296457?utm_source=blogtoedu

协程(yield,greenlet,gevent)

1.协程:在单线程中通过不断地在多个任务之间进行切换执行,来达到并发的效果的线程,可以提高效率

2.yield实现协程

#yield = return + generator(生成器)
#有yield的函数就是生成器,不会直接执行函数,首先获得生成器对象然后使用next函数这才开始运行函数
#next方法

#并发执行
import time
def productor():
    g = costumor()
    for i in range(100):
        res = next(g)
        print(res)

def costumor():
    for i in range(100):
        res = yield "*"*5+"%s"%i+'*'*5

if __name__ == '__main__':
    start_time = time.time()
    productor()
    end_time = time.time()
    print('%.9f'%float(end_time-start_time))


#串行
def productor():
    res = costumor()
    for i in res:
        print('*'*5+str(i)+"*"*5)

def costumor():
    res=[]
    for i in range(100):
        res.append(i)
    return res

if __name__ == '__main__':
    start_time = time.time()
    productor()
    end_time = time.time()
    print('%.9f'%float(end_time-start_time))

3.greenlet实现协程

#greenlet也是i一个可以实现单线程内并发的效果,但是和yield一样不能检测i/o模型
#切换的格式为:
'''
g1 = greenlet(function1)#获得对象
g2.switch(para)#从当前任务切换到g2对应的任务中
'''
#协程并发
from greenlet import greenlet

#定义函数一
def eat(name):
    print('%s1 is eating'%name)
    g2.switch('同学')
    print('%s2 is eating'%name)
    g2.switch()

#定义函数2
def play(name):
    print('%s1 is playing'%name)
    g1.switch()
    print('%s2 is playing'%name)


if __name__ == '__main__':
    g1 = greenlet(eat)
    g2 = greenlet(play)

    g1.switch('同学')

4.gevent实现协程


#1.gevent模块是基于greenlet模块的,具有检测自身I/O操作的功能
import gevent,time

#定义任务1
def eat(name):
    print('%s eat 1'%name)
    #自身的I/O操作。可以被检测出,一旦检测出就自动进行任务·切换
    gevent.sleep(3)
    print('%s eat 2'%name)

#定义任务2
def play(name):
    print('%s play 1'%name)
    gevent.sleep(4)
    print('%s play 2'%name)

start_time = time.time()
#创建gevent对象,只是提交了任务,要想出结果,必须跟join函数配对使用
g1 = gevent.spawn(eat,'john')
g2 = gevent.spawn(play,'lucy')

g1.join()
g2.join()
end_time = time.time()
print(end_time-start_time)
'''
运行结果:
john eat 1
lucy play 1
john eat 2
lucy play 2
4.0197389125823975#运行时间接近4秒,即两个任务并发执行的

#首先该程序是单线程,只有一个主线程,但是含有两个任务,首先按照代码从上到下执行,
执行到创建对象时,会自动跳转到对应的函数中去,如g1跳到eat函数,执行第一次打印,
检测到自身gevent.sleep()I/O操作后,就自动切换到下一个任务,执行play函数的第一次
打印,后面就是两个任务均处于等待状态,期间一直在两个任务之间切换进行检测,因为eat时间短,
所以先执行eat函数的第二次打印任务,接着就是Play的第二次打印

#因此这里实现了单线程两个任务并发的效果
'''


#2.gevent模块是基于greenlet模块的,具有检测自身I/O操作的功能,不能检测自身以外的I/O操作,如time.sleep()
import gevent,time

#定义任务1
def eat(name):
    print('%s eat 1'%name)
    #自身的I/O操作。可以被检测出,一旦检测出就自动进行任务·切换
    time.sleep(3)
    print('%s eat 2'%name)

#定义任务2
def play(name):
    print('%s play 1'%name)
    time.sleep(4)
    print('%s play 2'%name)

start_time = time.time()
#创建gevent对象,只是异步提交了任务,不会等结果的出来,要想出结果,必须跟join函数或者和joinall()配对使用,
g1 = gevent.spawn(eat,'john')
g2 = gevent.spawn(play,'lucy')

g1.join()
g2.join()
#gevent.joinall([g1,g2])
end_time = time.time()
print(end_time-start_time)
'''
john eat 1
john eat 2
lucy play 1
lucy play 2
7.019653797149658
#运行时间接近与两个任务I/O操作的时间总和,因此gevent不能检测出自身以为的I/O操作
'''

#3.gevent.monkey.patch_all():可以检测到自身以外的I/O操作
import gevent,time
from gevent import monkey
monkey.patch_all()

#定义任务1
def eat(name):
    print('%s eat 1'%name)
    #自身的I/O操作。可以被检测出,一旦检测出就自动进行任务·切换
    time.sleep(3)
    print('%s eat 2'%name)

#定义任务2
def play(name):
    print('%s play 1'%name)
    time.sleep(4)
    print('%s play 2'%name)

start_time = time.time()
#创建gevent对象,只是提交了任务,要想出结果,必须跟join函数配对使用
g1 = gevent.spawn(eat,'john')
g2 = gevent.spawn(play,'lucy')

g1.join()
g2.join()
end_time = time.time()
print(end_time-start_time)
'''
john eat 1
lucy play 1
john eat 2
lucy play 2
4.009758949279785#说明检测到了time.sleep()的I/O操作,自动进行切换,实现了单线程并发的效果
'''
发布了61 篇原创文章 · 获赞 11 · 访问量 893

猜你喜欢

转载自blog.csdn.net/qq_45769063/article/details/105143738