无法在期货列表上运行asyncio.wait(...)

时间:2019-07-31 04:43:57

标签: python-3.6 python-asyncio

我正在将协程提交到单独线程中的事件循环。当我依次使用future.next()等待每个未来时,所有这些都很好。但是,我现在要等待期货清单中第一个完成的期货。我正在尝试为此使用asyncio.wait(...),但似乎使用不正确。

下面是一个简化的示例。我在行TypeError: An asyncio.Future, a coroutine or an awaitable is required遇到异常done, pending = future.result()

如果我将[c1, c2, c3]传递给asyncio.wait([c1, c2, c3], return_when=asyncio.FIRST_COMPLETE),这是可行的,但是我是在随机时间提交任务,所以我只能收集期货集,而不是原始任务。并且文档明确指出您可以使用期货。

  

coroutine asyncio.wait(futures, *, loop=None, timeout=None, return_when=ALL_COMPLETED)

     

等待期货和序列期货给出的协程对象完成。协程将包装在“任务”中。返回两组Future :(已完成,待处理)。

import asyncio
import threading


async def generate():
    await asyncio.sleep(10)
    return 'Hello'


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


event_loop = asyncio.get_event_loop()
threading.Thread(target=lambda: run_loop(event_loop)).start()

c1 = generate()  # submitted at a random time
c2 = generate()  # submitted at a random time
c3 = generate()  # submitted at a random time
f1 = asyncio.run_coroutine_threadsafe(c1, event_loop)
f2 = asyncio.run_coroutine_threadsafe(c2, event_loop)
f3 = asyncio.run_coroutine_threadsafe(c3, event_loop)

all_futures = [f1, f2, f3]

# I'm doing something wrong in these 3 lines
waitable = asyncio.wait(all_futures, return_when=asyncio.FIRST_COMPLETED)
future = asyncio.run_coroutine_threadsafe(waitable, event_loop)
done, pending = future.result()  # This returns my TypeError exception

for d in done:
    print(d.result())

2 个答案:

答案 0 :(得分:1)

asyncio.wait期望 asyncio 期货并在事件循环内工作。要等待多个concurrent.futures期货(并在事件循环之外),请改用concurrent.futures.wait

done, pending = concurrent.futures.wait(
    all_futures, return_when=concurrent.futures.FIRST_COMPLETED)

请注意,如果您可以访问底层的异步期货,那么您的想法将会奏效。例如(未试用):

async def submit(coro):
    # submit the coroutine and return the asyncio task (future)
    return asyncio.create_task(coro)

# ...generate() as before

# note that we use result() to get to the asyncio futures:
f1 = asyncio.run_coroutine_threadsafe(submit(c1), event_loop).result()
f2 = asyncio.run_coroutine_threadsafe(submit(c2), event_loop).result()
f3 = asyncio.run_coroutine_threadsafe(submit(c3), event_loop).result()

# these should be waitable by submitting wait() to the event loop
done, pending = asyncio.run_coroutine_threadsafe(
    asyncio.wait([f1, f2, f3], return_when=asyncio.FIRST_COMPLETED)).result()

答案 1 :(得分:0)

此答案有助于回答此问题:

Create generator that yields coroutine results as the coroutines finish

asyncio.wait(...)不能接受期货,只能接受尚未安排的协程和待售商品。正确的方法是使用回调。将来完成时,它可以将自己添加到线程安全队列中,然后可以从该队列中退出。下面的示例解决了该问题:

import asyncio
import threading
import queue
import random


async def generate(i):
    await asyncio.sleep(random.randint(5, 10))
    return 'Hello {}'.format(i)


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


def done(fut):
    q.put(fut)


event_loop = asyncio.get_event_loop()
threading.Thread(target=lambda: run_loop(event_loop)).start()
q = queue.Queue()

c1 = generate(1)
c2 = generate(2)
c3 = generate(3)
f1 = asyncio.run_coroutine_threadsafe(c1, event_loop)
f2 = asyncio.run_coroutine_threadsafe(c2, event_loop)
f3 = asyncio.run_coroutine_threadsafe(c3, event_loop)
f1.add_done_callback(lambda fut: q.put(fut))
f2.add_done_callback(lambda fut: q.put(fut))
f3.add_done_callback(lambda fut: q.put(fut))

print(q.get().result())
print(q.get().result())
print(q.get().result())