greenlet的应用
import greenlet
import random
def producer():
while True:
item = random.randint(0,99)
print("生成数字:%d"%item)
c.switch(item)
def consumer():
while True:
item = p.switch() #从另一个协程获得它所传递的数据
print("消费数字:%d"%item)
if __name__ == "__main__":
c = greenlet.greenlet(consumer)
p = greenlet.greenlet(producer)
c.switch() #切换到c里面运行
#swtich()有参数就是将参数传递给另一个协程,否则就是激活协程
gevent的应用
以后协程的实现主要用gevent
gevent是第三方库,通过greenlet实现协程,其基本思想是:
当一个greenlet遇到IO操作时,比如访问网络,就自动切换到其他的greenlet,等到IO操作完成,再在适当的时候切换回来继续执行。由于IO操作非常耗时,经常使程序处于等待状态,有了gevent为我们自动切换协程,就保证总有greenlet在运行,而不是等待IO。
由于切换是在IO操作时自动完成,所以gevent需要修改Python自带的一些标准库,这一过程在启动时通过monkey patch完成。
greenlet对yield实现了封装,gevent对greenlet再次实现了封装
代码示例(和线程非常类似):
import gevent
from gevent.queue import Queue
import random
def Consume(queue):
while True:
gevent.sleep(random.random()) #gevent有一个特点,遇到了延时(如此处的gevent.sleep())就切换,没有遇到延时就不切换。那么以后在网络编程中,如recv等造成的延时时,就需要用gevent里面的recv
item=queue.get()
print("消费者,消费:%s"%item)
def Consume2(queue):
while True:
gevent.sleep(random.random())
item = queue.get()
print("消费者2,消费:%s" % item)
def Produce(queue):
while True:
gevent.sleep(random.random())
item = random.randint(0,99)
queue.put(item)
print("生产者,生产:%s"%item)
def Produce2(queue):
while True:
gevent.sleep(random.random())
item = random.randint(100, 199)
queue.put(item)
print("生产者2,生产:%s" % item)
queue = Queue(5)
p=gevent.spawn(Produce,queue) # spawn:产卵。此时不会执行
p2=gevent.spawn(Produce2,queue)
c=gevent.spawn(Consume,queue)
c2=gevent.spawn(Consume2,queue)
gevent.joinall([p,p2,c,c2]) #也可以通过p.join()、c.join()的方法
#
猴子补丁
作用:因为gevent要在遇到了gevent里面的延时操作时才切换,为了在不更改已有程序的基础上实现其切换,如:time.sleep(),就需要用到猴子补丁
import gevent
from gevent import monkey
import time
monkey.patch_all() # 会自己更改代码里面的延时操作
def f1(n):
for i in range(n):
print(gevent.getcurrent(), i)
time.sleep(0.5)
def f2(n):
for i in range(n):
print(gevent.getcurrent(), i)
time.sleep(0.5)
def f3(n):
for i in range(n):
print(gevent.getcurrent(), i)
time.sleep(0.5)
g1 = gevent.spawn(f1, 5)
g2 = gevent.spawn(f2, 5)
g3 = gevent.spawn(f3, 5)
gevent.joinall([g1, g2, g3])
协程函数(异步函数)
法一:async
#法一:
async def test1():
print(1)
await test2() #test1在此等待,转去执行test2,test2执行完毕后再到此处继续执行
print(2)
return "haha"
async def test2():
print(3)
print(4)
try:
a = test1()
a.send(None) #send None激活协程
except StopIteration as d:
print(d.value) #获得a的返回值“haha“
'''运行结果如下:
1
3
4
2
haha
'''
法二:asyncio
async def test1():
print(1)
await test2() #test1在此等待,转去执行test2,test2执行完毕后再到此处继续执行
print(2)
return "stop"
async def test2():
print(3)
print(4)
import asyncio
loop = asyncio.get_event_loop()
#创建一个task类型的对象(即任务)
task = asyncio.ensure_future(test1()) #将协程变成了task类型
loop.run_until_complete(task)
print(task.result()) #获得task类型的执行结果
'''运行结果如下:
1
3
4
2
stop
'''
一次执行多个任务
async def test1():
print(1)
await test2() #test1在此等待,转去执行test2,test2执行完毕后再到此处继续执行
#await必须和async结合使用,一旦有了await,就必须有async
print(2)
return "stop"
async def test2():
print(3)
print(4)
import asyncio
loop = asyncio.get_event_loop()
tasks = [
asyncio.ensure_future(test1()),
asyncio.ensure_future(test1()),
asyncio.ensure_future(test1()),
]
loop.run_until_complete(asyncio.wait(tasks))
for task in tasks:
print(task.result())
'''运行结果如下:
1
3
4
2
1
3
4
2
1
3
4
2
stop
stop
stop
'''
添加回调函数
async def test1():
print(1)
await test2() #test1在此等待,转去执行test2,test2执行完毕后再到此处继续执行
#await必须和async结合使用,一旦有了await,就必须有awati
print(2)
return "stop"
async def test2():
print(3)
print(4)
import asyncio
loop = asyncio.get_event_loop()
#通过回调函数获取 执行结果
def call_back(future):
print("回调结果:", future.result())
task = asyncio.ensure_future(test1())
task.add_done_callback(call_back)
loop.run_until_complete(task) #loop,当成一个线程池用即可(但是并不是线程池)
'''运行结果如下:
1
3
4
2
回调结果: stop
'''