调用ensure_future是否正确包装协同程序的结果?

时间:2018-06-17 22:03:07

标签: python python-asyncio

我还不确定何时使用ensure_future,我想知道这是否是我想要/需要它的情况。

考虑以下代码

import asyncio


loop = asyncio.get_event_loop()


async def do_work(count):
    for i in range(count):
        if i % 10000 == 0:
            await asyncio.sleep(0.1)

    print(count)
    return i


async def do_batch_work():
    res1 = do_work(100000)
    res2 = do_work(100)
    await asyncio.wait([res1, res2])
    return res1, res2


def main():
    loop = asyncio.get_event_loop()
    res1, res2 = loop.run_until_complete(do_batch_work())
    print(res1.result())
    print(res2.result())
    loop.close()

main()

打印:

100
100000
Traceback (most recent call last):
  File "more_async.py", line 30, in <module>
    main()
  File "more_async.py", line 26, in main
    print(res1.result())
AttributeError: 'coroutine' object has no attribute 'result'

异步代码按预期运行并按预期顺序打印出来,但loop.run_until_complete(...)并不能让我访问基础结果,因为coroutine对象似乎没有有办法得到结果。

我可以通过更改方法来解决这个问题

async def do_batch_work():
    res1 = asyncio.ensure_future(do_work(100000))
    res2 = asyncio.ensure_future(do_work(100))
    await asyncio.wait([res1, res2])
    return res1, res2

通过致电asyncio.ensure_future(...),我确保Task返回我可以致电result()的地方。

我想知道,这是处理它的正确方法吗?我是否需要使用ensure_future如果我关心该协程的结果,或者我还有其他不知道的方法吗?

2 个答案:

答案 0 :(得分:4)

你的代码非常好 - 它完全正常工作,我认为asyncio仍然没有开发出一些强大的标准。但是如果你只是想并行运行协同程序,还有另一种方法,而且更常见。

asyncio.gather()

Gather的文档非常不言自明,它会在结果列表中返回未来,以下是您的代码示例:

async def do_batch_work():
    res1 = do_work(100000)
    res2 = do_work(100)
    return await asyncio.gather(res1, res2)


def main():
    loop = asyncio.get_event_loop()
    res1, res2 = loop.run_until_complete(do_batch_work())
    print(res1)
    print(res2)
    loop.close()

答案 1 :(得分:2)

async def do_batch_work():
    res1 = do_work(100000)
    res2 = do_work(100)
    await asyncio.wait([res1, res2])
    return res1, res2

res1res2只是该示例中的协同程序,而不是Future个对象,因此没有结果属性。当您使用ensure_future() res1res2现在是Future个对象时,这就是您可以访问结果属性的原因。 wait()在两种情况下的工作方式相同,两个代码中的结果都是同等生成的,只是你的函数没有返回正确的对象。

如果您想修改第一个示例,则Future会将您想要的wait()个对象作为第一个项目返回。

async def do_batch_work():
    res1 = do_work(100000)
    res2 = do_work(100)
    (res1, res2), _ = await asyncio.wait([res1, res2])
    return res1, res2