因此,我使用Python asyncio
模塊(在Linux上)啓動子進程,然後對其進行異步監視。我的代碼工作正常......當在主線程上運行。但是,當我在工作線程上運行它時,它會掛起,並且從不調用回調函數。Python asyncio:在工作線程上運行subprocess_exec
我懷疑這可能實際上是某種未公開的缺陷或在工作線程上運行subprocess_exec
問題,可能與實現在後臺線程中處理信號的方式有關。但它也可能只是我搞砸了。
一個簡單的,可再現的例子是如下:因此,這裏
class MyProtocol(asyncio.SubprocessProtocol):
def __init__(self, done_future):
super().__init__()
self._done_future = done_future
def pipe_data_received(self, fd, data):
print("Received:", len(data))
def process_exited(self):
print("PROCESS EXITED!")
self._done_future.set_result(None)
def run(loop):
done_future = asyncio.Future(loop = loop)
transport = None
try:
transport, protocol = yield from loop.subprocess_exec(
lambda : MyProtocol(done_future),
"ls",
"-lh",
stdin = None
)
yield from done_future
finally:
if transport: transport.close()
return done_future.result()
def run_loop():
loop = asyncio.new_event_loop()
asyncio.set_event_loop(loop) # bind event loop to current thread
try:
return loop.run_until_complete(run(loop))
finally:
loop.close()
,我設置一個asyncio
事件循環執行該外殼命令ls -lh
,然後觸發當從子接收到的數據爲回調,以及子進程退出時的另一個回調。
如果我直接在Python程序的主線程中調用run_loop()
,一切都會正常。但是,如果我說:
t = threading.Thread(target = run_loop)
t.start()
t.join()
那麼什麼情況是,pipe_data_received()
回調調用成功,但process_exited()
則永遠不會調用,程序只是掛起。
周圍的Googling和尋找爲unix_events.py
實施asyncio
源代碼後,我發現它可能需要我的事件循環手動連接到全球「孩子守望者」對象,如下所示:
loop = asyncio.new_event_loop()
asyncio.set_event_loop(loop) # bind event loop to current thread
asyncio.get_child_watcher().attach_loop(loop)
顯然,子監視器是一個(無證)對象,負責在引擎蓋下(或類似的地方)調用waitpid
。但是,當我想這一點,並在後臺線程跑run_event_loop()
,我得到了錯誤:
File "/usr/lib/python3.4/asyncio/unix_events.py", line 77, in add_signal_handler
raise RuntimeError(str(exc))
RuntimeError: set_wakeup_fd only works in main thread
所以在這裏,它看起來像的實施實際上做了檢查,以確保信號處理程序只能在主用線程,導致我相信,在當前的實現中,在後臺線程上使用subprocess_exec
實際上,根本不可能不改變Python源代碼本身。
我正確嗎?不幸的是,asyncio
模塊的記錄很少,所以我很難對這裏的結論充滿信心。我可能只是在做錯事。