等效于Python的“异步def”函数中的“ yield”

时间:2018-08-23 07:30:24

标签: python-asyncio

我听说协程的.Select版本最终将在Python中弃用(也许在3.8之后),并且仅支持@asyncio.coroutine。今天,我使用装饰器版本是为了能够async def(而不是yield另一个协程),因为我正在等待某些更改(例如在yield from循环内轮询)或将大部分耗时的阻塞代码分成较小的块(从而改善并发体验)。

这是一个最小的示例:

while

这将打印以下内容:

import asyncio

@asyncio.coroutine
def foo(l):
    print('entering foo')
    while l[0] == 0:
        yield
    print('leaving foo')

async def bar(l):
    print('entering bar')
    await asyncio.sleep(1)
    l[0] = 1
    await asyncio.sleep(1)
    print('leaving bar')

async def main():
    l = [0]
    t0 = asyncio.ensure_future(foo(l))
    t1 = asyncio.ensure_future(bar(l))
    await t0
    await t1

loop = asyncio.get_event_loop()
loop.run_until_complete(main())

如何在entering foo entering bar # foo is polling # 1 second elapses leaving foo # 1 second elapses leaving bar 版的协程中实现这一目标?我们应该使用async def吗?

2 个答案:

答案 0 :(得分:4)

await asyncio.sleep(0)可以用来做您想做的事,但是在当前情况下,这不是一个好的解决方案。事件循环在执行的每个“滴答滴答”中都需要返回foo:这会浪费CPU资源。

当您要等待协程内部发生某些事情时,正确的方法是使用asyncio.Event

async def foo(event):
    print('entering foo')
    await event.wait()
    print('leaving foo')

async def bar(event):
    print('entering bar')
    await asyncio.sleep(1)
    event.set()
    await asyncio.sleep(1)
    print('leaving bar')

async def main():
    event = asyncio.Event()
    t0 = asyncio.ensure_future(foo(event))
    t1 = asyncio.ensure_future(bar(event))
    await t0
    await t1

loop = asyncio.get_event_loop()
loop.run_until_complete(main())

这样,事件循环将只返回一次:foo实际上已经被设置。

答案 1 :(得分:1)

是的,我认为await asyncio.sleep(0)是这样做的正确方法,请参见https://github.com/python/asyncio/issues/284。因此foo变为:

async def foo(l):
    print('entering foo')
    while l[0] == 0:
        await asyncio.sleep(0)
    print('leaving foo')