我的问题或多或少像this,这实际上是导致this的X-Y问题。但是,这不是重复的,因为我的用例略有不同,并且链接的线程无法回答我的问题。
我正在将一组同步程序从Java移植到Python。这些程序与异步库进行交互。在Java中,我可以阻止并等待该库的异步函数返回一个值,然后使用该值执行操作。
Python似乎在努力阻止我在任何地方阻止任何东西。 await
仅可用于async def
个函数,而这些函数又必须await
个功能。似乎没有内置的方法可以阻止async def
/ await
像病毒一样在我的代码中传播。
Task
和Future
没有任何内置的阻塞或wait_until_complete
机制,除非我想在Task.done()
上循环,这确实很糟糕。 / p>
我尝试了asyncio.get_event_loop().run_until_complete()
,但是会产生错误:This event loop is already running.
Apparently I'm not supposed to do that for anything except main()
.
The second linked question above建议使用单独的线程并在其中包装异步函数。我用一些简单的功能对其进行了测试,它似乎可以作为一个通用概念。这里的问题是我的异步库保留了对主线程的事件循环的引用,并在尝试从新线程got Future <Future pending> attached to a different loop
引用它时抛出错误。
我考虑过将对异步库的所有引用移动到一个单独的线程中,但是我意识到我仍然无法阻止新线程,因此我必须为该线程创建一个 third 线程。阻止通话,这会使我回到Future attached to a different loop
错误。
我在这里几乎没有想法。有没有一种方法可以阻止并等待异步函数返回,或者我真的被迫将整个程序转换为async
/ await
吗? (如果是后者,一个解释会很好。我不明白。)
答案 0 :(得分:0)
我花了一些时间,但终于找到了实际的问题?
是否有一种方法可以阻止并等待异步函数返回,或者我真的被迫将整个程序转换为异步/等待吗?
有一个高级功能asyncio.run()
。它做三件事:
它的源代码在这里:https://github.com/python/cpython/blob/3221a63c69268a9362802371a616f49d522a5c4f/Lib/asyncio/runners.py#L8您会发现它在后台使用了loop.run_until_complete(main)
。
我想,如果您正在编写完全异步的代码,则应该在asyncio.run()
函数末尾的某个地方调用main()
。但这不是必须的。您可以在任意位置运行它,多次。注意事项:
在给定线程中,一次只能有一个正在运行的事件循环
不要从async def
函数运行它,因为很明显,您已经在运行一个事件循环,因此您可以直接使用await
调用该函数
示例:
import asyncio
async def something_async():
print('something_async start')
await asyncio.sleep(1)
print('something_async done')
for i in range(3):
asyncio.run(something_async())
您可以使多个线程具有自己的事件循环:
import asyncio
import threading
async def something_async():
print('something_async start in thread:', threading.current_thread())
await asyncio.sleep(1)
print('something_async done in thread:', threading.current_thread())
def main():
t1 = threading.Thread(target=asyncio.run, args=(something_async(), ))
t2 = threading.Thread(target=asyncio.run, args=(something_async(), ))
t1.start()
t2.start()
t1.join()
t2.join()
if __name__ == '__main__':
main()
如果遇到此错误:Future attached to a different loop
可能表示以下两种情况:
您正在使用与另一个事件循环相关的资源,而不是现在正在运行的
您已经在启动事件循环之前创建了一些资源-在这种情况下,它使用了“默认事件循环”-但是,当您运行asyncio.run()
时,会启动一个不同环。我之前遇到过:asyncio.Semaphore RuntimeError: Task got Future attached to a different loop
您需要至少使用Python版本3.5.3-explanation here。