我试图理解python asyncio的call_soon_threadsafe API,但是失败了,下面的示例代码显示,如果我的simple
协程要返回某些东西,我该如何从调用方获取返回的值?
import time
import asyncio as aio
import uvloop
from threading import Thread
aio.set_event_loop_policy(uvloop.EventLoopPolicy())
async def simple(a, fut:aio.Future):
await aio.sleep(a)
return fut.set_result(a)
def delegator(loop):
aio.set_event_loop(loop)
loop.run_forever()
loop_exec = aio.new_event_loop()
t = Thread(target=delegator, args=(loop_exec,))
t.start()
if __name__ == '__main__':
start_time = time.time()
fut = loop_exec.create_future() # tried to get back returned value by future
handle = loop_exec.call_soon_threadsafe(aio.ensure_future, simple(3, fut))
res = aio.wait_for(fut, 10)
print('Time consumed: {}s'.format(time.time() - start_time))
print('>>>>>>>>>>', res)
# Output
Time consumed: 3.2901763916015625e-05s
>>>>>>>>>> <generator object wait_for at 0x110bb9b48>
如您所见,我正试图通过将未来传递给运行在不同线程中的协程来获取返回值,但仍然不知道如何正确获取它。
基本上两个问题:
call_soon_threadsafe
的实际用例是什么,只是觉得run_coroutine_threadsafe
更方便使用,并且能够涵盖我在这种不同的线程协程交互中可以想象的几乎所有情况。 答案 0 :(得分:1)
使用上面的示例代码,如何从调用方获取返回的值?
由于事件循环在主线程外运行,因此您需要使用线程感知的同步设备。例如:
async def simple(a, event):
await asyncio.sleep(a)
event.simple_result = a
event.set()
done = threading.Event()
loop_exec.call_soon_threadsafe(aio.ensure_future, simple(3, done))
done.wait(10)
res = done.simple_result
或者,您可以使用concurrent.futures.Future
进行同步,这就像是带有对象有效负载的一次性事件。 (请注意,您不能使用asyncio的将来,因为它是not thread-safe。)
async def simple(a, fut):
await asyncio.sleep(a)
fut.set_result(a)
done = concurrent.futures.Future()
loop_exec.call_soon_threadsafe(aio.ensure_future, simple(3, done))
res = done.result(10)
正如文森特(Vincent)在评论中指出的那样,run_coroutine_threadsafe
将会为您服务:
async def simple(a):
await asyncio.sleep(a)
return a
fut = asyncio.run_coroutine_threadsafe(simple(3))
res = fut.result(10)
此
call_soon_threadsafe
的实际用例是什么
最简单的答案是call_soon_threadsafe
是一个较低级别的API,仅在您要告诉事件循环执行或开始执行操作时使用。 call_soon_threadsafe
是用于实现类似run_coroutine_threadsafe
之类的功能的构建基块,还可以用于实现许多其他功能。至于为什么要自己使用该管道功能...
有时您要执行普通功能,而不是协程。有时,您的函数是一劳永逸的,而您并不关心它的返回值。 (或者该函数最终会通过一些辅助渠道通知您完成操作。)在这种情况下,call_soon_threadsafe
是正确的工作工具,因为它更轻巧,因为它不会尝试创建其他{ {1}}并将其附加到执行的代码。例子:
concurrent.futures.Future
告诉事件循环停止运行loop.call_soon_threadsafe(loop.stop)
将内容添加到无界异步队列中loop.call_soon_threadsafe(queue.put_nowait, some_item)
将协程提交给事件循环,而无需等待其完成loop.call_soon_threadsafe(asyncio.create_task, coroutinefn())
设置来自另一个线程的asyncio未来的结果