Python43_协程再讲

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
'''
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值