![](/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.