[英]Future from asyncio.run_coroutine_threadsafe hangs forever?
As a followup to my previous question about calling an async function from a synchronous one , I've discovered asyncio.run_coroutine_threadsafe
.作为我之前关于从同步函数调用异步函数的问题的后续,我发现了
asyncio.run_coroutine_threadsafe
。
On paper, this looks ideal.在纸面上,这看起来很理想。 Based on the comments in this StackOverflow question , this looks ideal.
根据此 StackOverflow question 中的评论,这看起来很理想。 I can create a new Thread, get a reference to my original event loop, and schedule the async function to run inside the original event loop while only blocking the new Thread.
我可以创建一个新线程,获取对原始事件循环的引用,并安排异步函数在原始事件循环内运行,同时仅阻塞新线程。
class _AsyncBridge:
def call_async_method(self, function, *args, **kwargs):
print(f"call_async_method {threading.get_ident()}")
event_loop = asyncio.get_event_loop()
thread_pool = ThreadPoolExecutor()
return thread_pool.submit(asyncio.run, self._async_wrapper(event_loop, function, *args, **kwargs)).result()
async def _async_wrapper(self, event_loop, function, *args, **kwargs):
print(f"async_wrapper {threading.get_ident()}")
future = asyncio.run_coroutine_threadsafe(function(*args, **kwargs), event_loop)
return future.result()
This doesn't error, but it doesn't ever return, either.这不会出错,但也不会返回。 The Futures just hang and the async call is never hit.
期货只是挂起,异步调用永远不会被命中。 It doesn't seem to matter whether I use a Future in
call_async_method
, _async_wrapper
, or both;我是否在
call_async_method
、 _async_wrapper
或两者中使用 Future 似乎并不重要; wherever I use a Future, it hangs.无论我在哪里使用 Future,它都会挂起。
I experimented with putting the run_coroutine_threadsafe
call directly in my main event loop:我尝试将
run_coroutine_threadsafe
调用直接放在我的主事件循环中:
event_loop = asyncio.get_event_loop()
future = asyncio.run_coroutine_threadsafe(cls._do_work_async(arg1, arg2, arg3), event_loop)
return_value = future.result()
Here too, the Future hangs.在这里,未来也悬而未决。
I tried using the LoopExecutor
class defined here , which seems like the exact answer to my needs.我尝试使用此处定义的
LoopExecutor
类,这似乎是我需要的确切答案。
event_loop = asyncio.get_event_loop()
loop_executor = LoopExecutor(event_loop)
future = loop_executor.submit(cls._do_work_async, arg1=arg1, arg2=arg2, arg3=arg3)
return_value = future.result()
There too, the returned Future hangs.在那里,返回的 Future 也挂了。
I toyed with the idea that I was blocking my original event loop and therefore the scheduled task would never run, so I made a new event loop:我想我会阻塞我原来的事件循环,因此计划任务永远不会运行,所以我做了一个新的事件循环:
event_loop = asyncio.get_event_loop()
new_event_loop = asyncio.new_event_loop()
print(event_loop == new_event_loop) # sanity check to make sure the new loop is actually different from the existing one - prints False as expected
loop_executor = LoopExecutor(new_event_loop)
future = loop_executor.submit(cls._do_work_async, arg1=arg1, arg2=arg2, arg3=arg3)
return_value = future.result()
return return_value
Still hanging at future.result()
and I don't understand why.仍然挂在
future.result()
,我不明白为什么。
What's wrong with asyncio.run_coroutine_threadsafe
/the way I'm using it? asyncio.run_coroutine_threadsafe
/我使用它的方式有什么问题?
I think there are two problems. 我认为有两个问题。 First one is that
run_coroutine_threadsafe
only submit the coroutine but not really run it. 第一个是
run_coroutine_threadsafe
仅提交协程而不真正运行它。
So 所以
event_loop = asyncio.get_event_loop()
future = asyncio.run_coroutine_threadsafe(cls._do_work_async(arg1, arg2, arg3), event_loop)
return_value = future.result()
doesn't work as you've never run this loop. 无法运行,因为您从未运行过此循环。
To make it work, theoretically, you can just use asyncio.run(future)
, but actually, you cannot, maybe it is because that it is submitted by run_coroutine_threadsafe
. 要使其工作,从理论上讲,您可以只使用
asyncio.run(future)
,但是实际上,您不能,也许是因为它是由run_coroutine_threadsafe
提交的。 The following will work: 以下将起作用:
import asyncio
async def stop():
await asyncio.sleep(3)
event_loop = asyncio.get_event_loop()
coro = asyncio.sleep(1, result=3)
future = asyncio.run_coroutine_threadsafe(coro, event_loop)
event_loop.run_until_complete(stop())
print(future.result())
The second problem is, I think you have noticed that your structure is somehow reversed. 第二个问题是,我认为您已经注意到您的结构有所改变。 You should run the event loop in the separated thread but submit the task from the main thread.
您应该在单独的线程中运行事件循环,但要从主线程提交任务。 If you submit it in the separated thread, you still need to run the event loop in the main thread to actually execute it.
如果在单独的线程中提交它,则仍然需要在主线程中运行事件循环才能实际执行它。 Mostly I would suggest just create another event loop in the separated thread.
通常,我建议只在单独的线程中创建另一个事件循环。
I have run into the same issue, and the answer by @Sraw did not help me, as in my case the coroutine also had to be done in the main event loop, monopolized by the async library.我遇到了同样的问题,@Sraw 的回答对我没有帮助,因为在我的情况下,协程也必须在主事件循环中完成,由异步库独占。
As a quick (though admittedly hacky) approach, what helped me is the nest_asyncio library, which patches asyncio
to allow nested event loops.作为一种快速(虽然公认是 hacky)的方法,帮助我的是nest_asyncio库,它修补了
asyncio
以允许嵌套事件循环。
With it, the following works for me, even if the event loop is already running:有了它,即使事件循环已经在运行,以下内容也适用于我:
import asyncio
import nest_asyncio
nest_asyncio.apply()
event_loop = asyncio.get_event_loop()
coro = asyncio.sleep(1, result=3)
print(event_loop.run_until_complete(coro))
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.