![](/img/trans.png)
[英]How to properly use asyncio run_coroutine_threadsafe function?
[英]Python asyncio run_coroutine_threadsafe never running coroutine?
我不确定我在这里做错了什么,我正在尝试创建一个包含队列并使用协程消耗该队列中项目的类。 loop.run_forever()
是,事件循环正在单独的线程中运行(在该线程中,我执行loop.run_forever()
使其运行)。
我所看到的是,用于消费物品的协程从未被解雇:
import asyncio
from threading import Thread
import functools
# so print always flushes to stdout
print = functools.partial(print, flush=True)
def start_loop(loop):
def run_forever(loop):
print("Setting loop to run forever")
asyncio.set_event_loop(loop)
loop.run_forever()
print("Leaving run forever")
asyncio.set_event_loop(loop)
print("Spawaning thread")
thread = Thread(target=run_forever, args=(loop,))
thread.start()
class Foo:
def __init__(self, loop):
print("in foo init")
self.queue = asyncio.Queue()
asyncio.run_coroutine_threadsafe(self.consumer(self.queue), loop)
async def consumer(self, queue):
print("In consumer")
while True:
message = await queue.get()
print(f"Got message {message}")
if message == "END OF QUEUE":
print(f"exiting consumer")
break
print(f"Processing {message}...")
def main():
loop = asyncio.new_event_loop()
start_loop(loop)
f = Foo(loop)
f.queue.put("this is a message")
f.queue.put("END OF QUEUE")
loop.call_soon_threadsafe(loop.stop)
# wait for the stop to propagate and complete
while loop.is_running():
pass
if __name__ == "__main__":
main()
输出:
Spawaning thread Setting loop to run forever in foo init Leaving run forever
此代码存在多个问题。
首先,检查警告:
test.py:44: RuntimeWarning: coroutine 'Queue.put' was never awaited
f.queue.put("this is a message")
test.py:45: RuntimeWarning: coroutine 'Queue.put' was never awaited
f.queue.put("END OF QUEUE")
这意味着queue.put是协程,因此必须使用run_coroutine_threadsafe运行:
asyncio.run_coroutine_threadsafe(f.queue.put("this is a message"), loop)
asyncio.run_coroutine_threadsafe(f.queue.put("END OF QUEUE"), loop)
您也可以使用queue.put_nowait这是一个同步方法。 但是,异步对象通常不是线程安全的,因此每个同步调用都必须通过call_soon_threadsafe进行 :
loop.call_soon_threadsafe(f.queue.put_nowait, "this is a message")
loop.call_soon_threadsafe(f.queue.put_nowait, "END OF QUEUE")
另一个问题是,在使用者任务可以开始处理项目之前,循环已停止。 您可以向Foo
类添加一个join
方法,以等待使用者完成:
class Foo:
def __init__(self, loop):
[...]
self.future = asyncio.run_coroutine_threadsafe(self.consumer(self.queue), loop)
def join(self):
self.future.result()
然后确保在停止循环之前调用此方法:
f.join()
loop.call_soon_threadsafe(loop.stop)
这应该足以使程序按预期工作。 但是,此代码在几个方面仍然存在问题。
首先,不应同时在主线程和额外线程中设置循环。 Asyncio循环不是要在线程之间共享,因此您需要确保与asyncio相关的所有事情都在专用线程中发生。
由于Foo
负责这两个线程之间的通信,因此您必须格外小心,以确保每一行代码都在正确的线程中运行。 例如, asyncio.Queue
的实例化必须发生在asyncio线程中。
有关程序的正确版本,请参见此要点 。
另外,我想指出,这不是异步的典型用例。 您通常希望在主线程中运行asyncio循环,尤其是在需要子流程支持的情况下 :
asyncio支持从不同线程运行子进程,但是有一些限制:
- 事件循环必须在主线程中运行
- 在从其他线程执行子进程之前,必须在主线程中实例化子监视程序。 在主线程中调用get_child_watcher()函数以实例化子监视程序。
我建议您以另一种方式设计应用程序,即在主线程中运行asyncio并将run_in_executor用于同步阻塞代码。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.