2017-06-21 93 views
4

因此,我使用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模塊的記錄很少,所以我很難對這裏的結論充滿信心。我可能只是在做錯事。

回答

4

只要一ASYNCIO循環和其子觀察家主線程運行在輔助線程處理子過程是好的實例化:

asyncio.get_child_watcher() 
loop = asyncio.get_event_loop() 
coro = loop.run_in_executor(None, run_loop) 
loop.run_until_complete(coro) 

this postdocumentation