我有两个共享某些状态的Python线程A
和B
。某一时刻,A
提交了一个由B
在其循环中运行的回调,例如:
# This line is executed by A
loop.call_soon_threadsafe(callback)
此后,我想继续执行其他操作,但是在执行此操作之前,请确保callback
已运行B
。有什么方法(除了标准线程同步原语之外)可以使A
等待回调完成?我知道call_soon_threadsafe
返回了一个asyncio.Handle
对象,该对象可以取消任务,但是我不确定该对象是否可以用于等待(我对asyncio
还是不太了解)。 / p>
在这种情况下,此回调调用loop.close()
并取消剩余的任务,此后,在B
中,在loop.run_forever()
之后有一个loop.close()
。因此,对于这种用例,尤其是一种线程安全机制,允许我从A
知道何时有效地关闭了循环,这对我也同样适用-再次,不涉及互斥锁/条件变量/等。>
我知道asyncio
并不是线程安全的,只有少数例外,但是我想知道是否提供了一种方便的方法来实现这一目标。
这里是我的意思的一小段,以防万一。
import asyncio
import threading
import time
def thread_A():
print('Thread A')
loop = asyncio.new_event_loop()
threading.Thread(target=thread_B, args=(loop,)).start()
time.sleep(1)
handle = loop.call_soon_threadsafe(callback, loop)
# How do I wait for the callback to complete before continuing?
print('Thread A out')
def thread_B(loop):
print('Thread B')
asyncio.set_event_loop(loop)
loop.run_forever()
loop.close()
print('Thread B out')
def callback(loop):
print('Stopping loop')
loop.stop()
thread_A()
我用asyncio.run_coroutine_threadsafe
尝试了这种变化,但是它不起作用,而是线程A
永远挂起。不知道我是在做错什么,还是因为我正在停止循环。
import asyncio
import threading
import time
def thread_A():
global future
print('Thread A')
loop = asyncio.new_event_loop()
threading.Thread(target=thread_B, args=(loop,)).start()
time.sleep(1)
future = asyncio.run_coroutine_threadsafe(callback(loop), loop)
future.result() # Hangs here
print('Thread A out')
def thread_B(loop):
print('Thread B')
asyncio.set_event_loop(loop)
loop.run_forever()
loop.close()
print('Thread B out')
async def callback(loop):
print('Stopping loop')
loop.stop()
thread_A()
答案 0 :(得分:3)
设置了回调,并且(大部分)忘记了。它们不适用于您需要从中获得结果的东西。这就是为什么生成的句柄仅允许您取消回调(不再需要此回调),仅此而已。
如果您需要在另一个线程中等待异步管理的协程的结果,请使用协程并将其作为任务与asyncio.run_coroutine_threadsafe()
计划;这会给您Future()
instance,然后您可以等待完成。
但是,用run_coroutine_threadsafe()
停止循环确实需要循环处理比实际运行多一轮的回调。由Future
返回的run_coroutine_threadsafe()
不会被通知其计划任务的状态变化。您可以通过在关闭循环之前在线程B中运行asyncio.sleep(0)
至loop.run_until_complete()
来解决此问题:
def thread_A():
# ...
# when done, schedule the asyncio loop to exit
future = asyncio.run_coroutine_threadsafe(shutdown_loop(loop), loop)
future.result() # wait for the shutdown to complete
print("Thread A out")
def thread_B(loop):
print("Thread B")
asyncio.set_event_loop(loop)
loop.run_forever()
# run one last noop task in the loop to clear remaining callbacks
loop.run_until_complete(asyncio.sleep(0))
loop.close()
print("Thread B out")
async def shutdown_loop(loop):
print("Stopping loop")
loop.stop()
当然,这有点怪异,并且取决于回调管理和跨线程任务计划的内部结构是否保持不变。按照默认的asyncio
实现而言,运行一次noop任务足以进行几轮回调,从而创建更多要处理的回调,但是替代循环实现可能会对此有所不同。
因此,对于关闭循环,使用基于线程的协调可能会更好:
def thread_A():
# ...
callback_event = threading.Event()
loop.call_soon_threadsafe(callback, loop, callback_event)
callback_event.wait() # wait for the shutdown to complete
print("Thread A out")
def thread_B(loop):
print("Thread B")
asyncio.set_event_loop(loop)
loop.run_forever()
loop.close()
print("Thread B out")
def callback(loop, callback_event):
print("Stopping loop")
loop.stop()
callback_event.set()
答案 1 :(得分:1)
有什么方法(除了标准线程同步原语之外)使A等待回调完成?
通常您会使用Martijn最初建议的run_coroutine_threadsafe
。但是您使用loop.stop()
会使回调有些特定。鉴于此,最好使用标准线程同步原语,它在这种情况下非常简单,可以与回调实现和代码的其余部分完全脱钩。例如:
def submit_and_wait(loop, fn, *args):
"Submit fn(*args) to loop, and wait until the callback executes."
done = threading.Event()
def wrap_fn():
try:
fn(*args)
finally:
done.set()
loop.call_soon_threadsafe(wrap_fn)
done.wait()
使用loop.call_soon_threadsafe(callback)
,而不要使用submit_and_wait(loop, callback)
。线程同步在那里,但是完全隐藏在submit_and_wait
内部。