正确使用loop.create_future

14

我正在阅读Python文档和PyMotW书籍,试图学习Async/Await、Futures和Tasks。

协程和任务文档

通常在应用程序级别的代码中没有必要创建Future对象。

future文档中可以得知:

loop.create_future()

创建一个附加到事件循环的asyncio.Future对象。

这是在asyncio中创建Future的首选方法。这让第三方事件循环提供了替代实现Future对象(具有更好的性能或仪表化)的可能性。

然而,在PyMotW Future章节中,作者像这样创建了一个future对象:

all_done = asyncio.Future()

我认为这是因为该书略落后于当前Python版本。为了纠正这个问题,我进行了以下操作:

future_Obj = event_loop.create_future()

所以作者的完整代码如下:

import asyncio


def mark_done(future, result):
    print('setting future result to {!r}'.format(result))
    future.set_result(result)


event_loop = asyncio.get_event_loop()

try:

    future_Obj = event_loop.create_future()
    print('scheduling mark_done')
    event_loop.call_soon(mark_done, future_Obj, 'the result')

    print('entering event loop')
    result = event_loop.run_until_complete(future_Obj)
    print('returned result: {!r}'.format(result))
finally:
    print('closing event loop')
    event_loop.close()

print('future result: {!r}'.format(future_Obj.result()))

问题:

根据文档,上面示例中的future_Obj = event_loop.create_future()是创建一个正确的future对象的方式吗?

1个回答

8
在上面的示例中,future_Obj = event_loop.create_future() 是根据文档创建Future对象的正确方式。
需要注意的一件事是,Future与事件循环相关联,因此在顶层创建一个Future会创建一个与asyncio.get_event_loop()最初返回的循环相关联的Future。一旦你转换到asyncio.run,你将会得到一个错误,因为每次调用asyncio.run都会创建一个新的事件循环。
为了避免这个问题,顶层Future可以一开始设置为None,并在协程内部使用global适当地进行创建。由于你明确地传递了Future(遵循良好的实践),所以根本不需要全局变量。
def mark_done(future, result):
    print('setting future result to {!r}'.format(result))
    future.set_result(result)

async def main():
    loop = asyncio.get_event_loop()
    future = loop.create_future()
    print('scheduling mark_done')
    loop.call_soon(mark_done, future, 'the result')
    print('suspending the coroutine')
    result = await future
    print('awaited result: {!r}'.format(result))
    print('future result: {!r}'.format(future.result()))
    return result

if __name__ == '__main__':
    print('entering the event loop')
    result = asyncio.run(main())
    print('returned result: {!r}'.format(result))

请注意,使用 asyncio.run 时,您永远不需要显式关闭循环,这是自动完成的。如果您正在使用 Python 3.6 或更早版本,则可以使用 asyncio.get_event_loop().run_until_complete(main()) 替换 asyncio.run(main())

只是出于好奇,在 Python 3.7 之前,创建 future 对象的方式是否像这样 all_done = asyncio.Future()?作者运行一个错误跟踪器,所以我们可以告诉他在 Python 3.7 中该行已经更改。 - user10597450
create_future事件循环方法是在Python 3.5.2中引入的,因此它并不是3.7的新功能。指南中更严重的问题是,在现代异步编程中,创建和操作原始future被认为是不好的风格。几乎所有使用x.add_done_callback(foo)的示例都可以通过将其表达为协程来受益,该协程等待x然后执行foo - user4815162342
我应该放弃那个指南/书籍作为学习资源吗?如果是这样,还有哪些资源可以学习异步/等待、任务和未来? - user10597450
@EmilyScott 我不会那么说,情况并没有那么糟。至于其他资源,这里是我在学习asyncio时编写的阅读列表。也许它会有所帮助。 - user4815162342
谢谢你,我已经收藏了 :D。你甚至在中级部分下有 PyMotW。 - user10597450
我提出了一个更详细的问题,关于为什么创建和使用 futures 是不好的实践,你能帮忙吗?这个问题是基于你对我之前问题的回答。 - user10597450

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接