首页投稿(暂停使用,暂停投稿)Python 运维生活不易 我用python

理解python异步机制

2016-09-13  本文已影响2287人  treelake

1. python yield与async/await

要点

最重要的是生成器函数碰到yield停止执行,收到next或send才会继续执行的机制。
而且send方法令我们可以传递值到生成器暂停的地方。
生成器执行结束抛出StopIteration 异常。
yield from用于把其他生成器当做子例程调用。

MUST: yield from + iterable
MUST: @asyncio.coroutines + yield from / yield
MUST: await + coroutine / object with await() method (that must ruturn a iterable instead of a coroutine)
MUSTN'T: async def + yield/yield from

图说

由yield到async/await 由yield到async/await变化标示

2. async/await 实验

实验一:最简调度器

@types.coroutine
def switch():
    yield

然后它可以被其他用async def定义的的协程函数B和C await,只有当await返回时,B和C才继续执行。
这样我们就可以有效地控制B和C的执行顺序。
然后我们创建了一个调度器,它对列表进行了两次深拷贝以避免问题。它循环协程队列,使用send方法对每个协程依次递进,如果有协程已经完成则将其移出队列,当列表中的协程全部完成时结束。

实验二:具有睡眠功能的简单调度器

async def sleep(delay):
    await switch(delay=delay)

然后通过args=coro.send(None)与该函数碰撞,得到含有delay参数的字典作为send的返回值。便可以判断出是否调用调度器的睡眠机制。
最后在调度器中实现每一次协程列表循环结束后判断在睡眠列表中的协程是否有到时间的,到时间或时间超出则添加到运行协程列表中进入循环执行。如果运行列表中的协程都执行完了,则查看睡眠列表中的协程中还需睡眠的最少时间,线程睡眠,睡眠完成再将其添加到运行队列。

实验三:使能增加新的协程

实验四:和线程池进行交互

def background(fn):  
    @wraps(fn)
    async def wrapper(*args,**kwargs):
        return await switch(op='background',fn=fn,args=args,kwargs=kwargs)
    return wrapper

该装饰器能将一个比较耗时的计算函数封装为一个协程,使其可以被其他协程await。在调度器中利用send函数的返回值可以获取它的类型为background、函数入口地址以及函数的传参,然后在调度器中按相应机制执行。
第二部分是在调度器中的修改:我们让调度器类拥有了一个私有的concurrent.futures.ThreadPoolExecutor()对象。并在运行协程队列的循环判断中将background类型的操作提交给线程池对象,并将当前的协程移出运行队列,添加到futures队列中。然后在每次运行队列循环后判断futures中的任务是否有完成的(使用的参数为一旦有任一任务完成或被取消都返回),如果主线程此时处于将要睡眠的状态,就等待相应的时间,没有的话则立刻返回,下次再查询,完成的任务将其所在协程带入运行队列,任务结果通过调度器send传回该协程。

实验五:

部分结果,可以看出异步执行的效果

实验六:

程序结构示意图

相关知识点

上一篇下一篇

猜你喜欢

热点阅读