一、yield实现异步
yield在python中初学时,觉得比较难理解。yield的作用:
①返回一个值、②接收调用者的参数
分析下面的代码:
#!/usr/bin/env python3
# -*- coding:utf-8 -*-
def consumer():
r = ''
while True:
n = yield r
print("[Consumer] n = %d" %n)
if not n:
return
print("[Consumer] consuming %s..." %n)
r = '200 OK'
def produce(c):
c.send(None)
h = 0
while h < 5:
h = h + 1
print("[Producer] producing %d..." %h)
s = c.send(h)
print("[Producer] consumer return: %s" %s)
c.close()
c = consumer() #创建一个生成器
produce(c) #在该函数中,调用生成器的send()方法
结合程序运行过程,可分析出:
第一步:在produce(c)函数中,调用了c.send(None)启动了生成器,遇到yield暂停;接着执行produce()中接下来的代码,从运行结果看,确实打印出了[Produce] producing 1 … 当程序运行至c.send(h)时,调用生成器并且通过yield传递了参数(h = 1)进入consumer()函数执行。
第二步,yield传递参数(h=1)给consumer()函数中的n,并接着上一次暂停处往下继续执行,打印出[Consumer] n = 1,[Consumer] consuming 1… ;在consumer()函数中此时 r 被赋值为’200 OK’,接着循环遇到yield, consumer()函数又暂停并且返回变量 r 的值,此时程序又进入produce(c)函数中接着执行。
第三步,produce(c)函数接着第一步中c.send(h)处,继续往下执行打印出[Producer] consumer return: 200 OK,并进行循环,打印[Producer] producing 2… 后,又调用c.send(h) 。。。如此循环回到第一步!
转载自:https://blog.csdn.net/u012730382/article/details/54135426
二、yield + 装饰器实现
import time
import _thread
def gen_coroutine(f):
def wrapper(*args, **kwargs):
gen_f = f() # gen_f为生成器req_a
r = next(gen_f) # r为生成器long_io
def fun(g):
ret =next(g) # 执行生成器long_io
try:
gen_f.send(ret) # 将结果返回给req_a并使其继续执行
except StopIteration:
pass
_thread.start_new_thread(fun, (r,))
return wrapper
def long_io():
print("开始执行IO操作")
time.sleep(5)
print("完成IO操作,yield回操作结果")
yield "io result"
@gen_coroutine
def req_a():
print("开始处理请求req_a")
ret = yield long_io()
print("ret: %s" % ret)
print("完成处理请求req_a")
def req_b():
print("开始处理请求req_b")
time.sleep(2)
print("完成处理请求req_b")
def main():
req_a()
req_b()
while 1:
pass
if __name__ == '__main__':
main()
三、yield结合装饰器(无返回值)
def deco(func):
def wrapper():
yie = func()
print(type(yie))
ret = next(yie)
return yie
return wrapper
@deco
def foo():
while True:
x = yield
print("x-->",x)
g = foo()
g.send(1)
g.send(2)
输出如下:
<class 'generator'>
x--> 1
x--> 2
以上代码的大概意思是:
@deco是装饰器的语法糖模式,foo()是执行deco函数中wrapper函数,yie = func()是返回一个generator生成器,然后next(yie),这里原因是g.send不能用在第一个触发生成器 ,然后在main线程里面,g = foo()返回一个生成器,然后g.send(1) g.send(2)就输出foo()函数的打印输出
四、yield结合装饰器(有返回值)
我们在来看一个yield带返回值的
print("--------------------")
def deco(func):
def wrapper():
res = func()
next(res)
return res
return wrapper
@deco
def foo():
food_list = []
while True:
food = yield food_list #返回添加food的列表
food_list.append(food)
print("elements in foodlist are:",food)
g = foo()
print(g.send('苹果'))
print(g.send('香蕉'))
re = g.send('菠萝')
输出如下:
E:\python\python_sdk\python.exe E:/python/py_pro/python.py
--------------------
elements in foodlist are: 苹果
['苹果']
elements in foodlist are: 香蕉
['苹果', '香蕉']
elements in foodlist are: 菠萝
Process finished with exit code 0
以上的代码g.send(‘xx”)返回一个food_list,然后输出列表信息即可
五、yield实现并发效果
以上的yield是跟装饰器结合使用,我们来看看yield能不能实现,程序遇到io实现切换?
以下的例子是生产者生产数据,消费者消费数据
#基于yield并发执行
import time
def consumer():
'''任务1:接收数据,处理数据'''
while True:
x=yield
print("consumer拿到数据了x:",x)
def producer():
'''任务2:生产数据'''
g=consumer()
next(g)
for i in range(10):
print("producer生产数据了",i)
g.send(i)
start=time.time()
#基于yield保存状态,实现两个任务直接来回切换,即并发的效果
#PS:如果每个任务中都加上打印,那么明显地看到两个任务的打印是你一次我一次,即并发执行的.
producer()
stop=time.time()
print(stop-start)
代码输出:
E:\python\python_sdk\python.exe E:/python/py_pro/python.py
producer生产数据了 0
consumer拿到数据了x: 0
producer生产数据了 1
consumer拿到数据了x: 1
producer生产数据了 2
consumer拿到数据了x: 2
producer生产数据了 3
consumer拿到数据了x: 3
producer生产数据了 4
consumer拿到数据了x: 4
producer生产数据了 5
consumer拿到数据了x: 5
producer生产数据了 6
consumer拿到数据了x: 6
producer生产数据了 7
consumer拿到数据了x: 7
producer生产数据了 8
consumer拿到数据了x: 8
producer生产数据了 9
consumer拿到数据了x: 9
0.0
Process finished with exit code 0
基于yield保存状态,实现两个任务直接来回切换,即并发的效果
PS:如果每个任务中都加上打印,那么明显地看到两个任务的打印是你一次我一次,即并发执行的.
六、yield不能实现io切换
yield是实现了并发的效果,但是它能实现,在io阻塞,进行任务间切换么?我们来看一个例子
import time
def consumer():
'''任务1:接收数据,处理数据'''
while True:
x=yield
print("consumer拿到数据了x:", x)
def producer():
'''任务2:生产数据'''
g=consumer()
next(g)
for i in range(3):
print("producer生产数据了")
g.send(i)
time.sleep(2)
start=time.time()
producer() #并发执行,但是任务producer遇到io就会阻塞住,并不会切到该线程内的其他任务去执行
stop=time.time()
print(stop-start)
输出结果:
producer生产数据了
consumer拿到数据了x: 0
(有io阻塞)
producer生产数据了
consumer拿到数据了x: 1
(有io阻塞)
producer生产数据了
consumer拿到数据了x: 2
6.001317262649536
通过输出效果显示,并不能
七、greenlet任务切换
如果我们在单个线程内有20个任务,要想实现在多个任务之间切换,使用yield生成器的方式过于麻烦(需要先得到初始化一次的生成器,然后再调用send。。。非常麻烦),而使用greenlet模块可以非常简单地实现这20个任务直接的切换
greenlet只是提供了一种比generator更加便捷的切换方式,当切到一个任务执行时如果遇到io,那就原地阻塞,仍然是没有解决遇到IO自动切换来提升效率的问题。
单纯的切换(在没有io的情况下或者没有重复开辟内存空间的操作),反而会降低程序的执行速度
from greenlet import greenlet
def eat(name):
print('%s eat 1' %name)
g2.switch('aaa')
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('safly')#可以在第一次switch时传入参数,以后都不需要
输出如下:
E:\python\python_sdk\python.exe E:/python/py_pro/python.py
safly eat 1
aaa play 1
safly eat 2
aaa play 2
Process finished with exit code 0
转载自:https://blog.csdn.net/u013210620/article/details/78740042