从执行器等待 Future:Future 不能在 'await' 表达式中使用

25

我想在一个Python协程中使用ThreadPoolExecutor,将一些阻塞的网络调用委托给一个单独的线程。然而,运行以下代码:

from concurrent.futures import ThreadPoolExecutor
import asyncio

def work():
  # do some blocking io
  pass

async def main():
  executor = ThreadPoolExecutor()
  await executor.submit(work)

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

引起错误:
TypeError: object Future can't be used in 'await' expression

Future对象不能被await吗?为什么会这样说?

我该如何await一个由executor.submit返回的Future对象?

Python 3.5.0

编辑

使用executor.submit不是我的决定。这在多个库中被内部使用,例如requests-futures。我正在寻找一种从协程中与这些模块进行交互的方法。

1个回答

37

您应该使用 loop.run_in_executor

from concurrent.futures import ThreadPoolExecutor
import asyncio

def work():
  # do some blocking io
  pass

async def main(loop):
  executor = ThreadPoolExecutor()
  await loop.run_in_executor(executor, work)

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

编辑

concurrent.futures.Future 对象与 asyncio.Future 不同。 asyncio.Future 用于事件循环,可使用awaitable,而前者则不行。 loop.run_in_executor 提供了两者之间必要的互操作性。

编辑 #2

使用 executor.submit 不是我的决定。这是由多个库(例如 requests-futures)在内部使用的。我正在寻找一种从协程中与这些模块互操作的方法。

编辑 #3

自 Python 3.5(文档)起,您可以使用 asyncio.wrap_future(future, *, loop=None)concurrent.futures.Future 转换为 asyncio.Future


1
我不知道为什么会被踩。这肯定是有效的。但是我更感兴趣的是“为什么”,而不是“如何”。此外,我使用futures-requests库,它在内部使用submit。 - Tamas Hegedus
1
简单来说,concurrent.futures.Future 不可等待。你可能想要使用的是 asyncio.Future - Jashandeep Sohi
1
好的,我们有不同类型称为“Future”。这些类型之间是否有转换? - Tamas Hegedus
2
asyncio.wrap_future 可能是你正在寻找的。虽然我不确定它是否公开或在文档中列出。 - Jashandeep Sohi
asyncio.wrap_future 在此文档中有记录(但该文档未提及@JashandeepSohi未提到的任何内容)。 - acl
这非常有帮助,我很高兴找到了这个解释。但是我认为,在Python标准库中拥有两个不同的“Future”类是一个糟糕的设计选择。 - Reubend

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