【问题标题】:Compose futures in Python (asyncio)用 Python 编写期货(asyncio)
【发布时间】:2018-07-09 21:07:50
【问题描述】:

我正在尝试在 Python 中实现一个简单的演员系统,以深入了解并发编程。

我主要关心的是使用 asyncio 模块在 Python 中编写期货的通用任务。

假设我有一个在单独的线程上运行的事件循环。

import asyncio

from threading import Thread

loop = asyncio.new_event_loop()


def run_event_loop(loop):
    asyncio.set_event_loop(loop)
    loop.run_forever()


t = Thread(target=run_event_loop, args=(loop,))
t.start()

为了避免同步通信,我需要能够以某种方式编写Futures。举个简单的例子,假设我想合并两个 async 协程的结果。

我的第一次(显然是错误的)尝试:

async def f(x):
    fut = asyncio.run_coroutine_threadsafe(g(x / 2), loop)
    return fut

async def g(x):
    return x * 2

f(x) = x / 2

g(x) = x * 2

f(g(x)) = x

我无法得出一个干净的解决方案来处理以下一般问题:

x = 10

future = asyncio.run_coroutine_threadsafe(f(x), loop)

print(future)                    # <Future at 0x106f797f0 state=pending>

print(future.result())           # <Future at 0x10677c780 state=finished returned float>

print(future.result().result())  # 10.0

理想情况下,我应该结合期货并在最后执行一个future.result()。或者,也许我对这一切的思考都错了。

【问题讨论】:

  • 您是在询问来自concurrent.futures 的期货,正如您的标题、标签和描述所暗示的那样,还是来自asyncio 的期货,正如您的示例代码所暗示的那样?正如文档明确指出的那样,它们不是同一类型。 asyncio 中的那个模仿了futures 中的那个,但它们不能互换使用。 (请注意,有一个 asyncio.Executor 将并发期货包装在异步期货中;您永远不应该直接在异步代码中使用并发期货。)
  • 我编辑了标题。 type(future) 给了&lt;class 'concurrent.futures._base.Future'&gt;,所以我错误地认为它们是相同的
  • 不,这 is 是一个并发的未来,因为您正在使用 run_coroutine_threadsafe 并将结果返回到不同的线程中。将两者混合在一起会令人困惑。
  • 无论如何,f 是否真的需要像这样运行g 线程安全,即使它实际上是在同一线程中的同一事件循环上运行g?如果是这样,您不能直接将生成的并发未来与 asyncio 未来组合在一起,我认为这就是您要解决的问题。如果您不需要它,那么您的问题就会消失。
  • 你说得对我只需要做res = await g(x / 2)

标签: python concurrency python-asyncio


【解决方案1】:

这里的根本问题是:

fut = asyncio.run_coroutine_threadsafe(g(x / 2), loop)

您在 async 函数中,并试图在同一线程的同一事件循环中运行另一个异步函数。你不需要run_coroutine_threadsafe。就await吧。

如果您仍然拨打run_coroutine_threadsafe 会发生什么?然后你得到的是concurrent.futures.Future。正如asyncio 文档顶部所解释的,这与asnycio.Future 不同——后者旨在模仿一个并发的未来,但在幕后它是另一回事。您可以组合两个 asyncio 未来(来自同一个事件循环),您可以组合两个并发未来,但不能用异步未来组合一个并发未来。

您可以将并发未来包装在异步未来中(直接使用wrap_future,或者更常见的是间接通过run_in_executor)。但是你最终会得到一个并发未来持有一个异步未来包装一个并发未来,它仍然无法组合。

【讨论】:

  • 我认为最后一句话倒过来了:如果将并发未来包装在异步未来中,他们最终会得到一个异步未来(包装了一个包装异步未来的并发未来)。由于wrap_futurerun_coroutine_threadsafe 中的至少一个期望期货在单独的线程中运行,但it seems to work,我有一半的预期是等待这样的未来会陷入僵局。你剩下的答案当然是完全正确的。
  • @user4815162342 你有两次调用run_coroutine_threadsafe,所以你天生就有一个外部并发未来和一个内部并发;如果你先包装内部的,你有一个并发包装和异步包装一个并发。而且你不能在顶层之外有另一个异步,因为你在主线程中没有事件循环。
  • 你说得对,我错过了问题的细节,这确实没有多大意义。 (但您的回答非常有助于消除混淆。)我在最后一段中提到了在使用run_coroutine_threadsafe 获得的并发未来上使用wrap_future 的一般情况,这实际上似乎有效,即使是浪费和不必要的.
  • @user4815162342 不管怎样,起初我很惊讶没有死锁,但考虑一下:你有一个聊天服务器在一个线程池上运行,每个线程池都运行一个事件循环。用户 X 的 coro 想要为用户 Y 等待某些东西,因此它会在 Y 所在的任何循环上执行线程安全运行。如果当 X 和 Y 在同一个循环中时发生死锁,您需要编写额外的代码来区分这种情况。如果 Guido 想到这一点,并将额外的代码放入库中以支持等待来自您自己的线程的并发未来,它也会处理这种情况。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-11-05
  • 2015-01-09
  • 1970-01-01
  • 1970-01-01
  • 2019-06-14
  • 2019-05-14
相关资源
最近更新 更多