2017-06-21 1 views
4

私は、Python asyncioモジュール(Linux上)を使用して子プロセスを起動し、それを非同期に監視しています。私のコードは正常に動作します... メインスレッドで実行すると。しかし、ワーカースレッド上で実行すると、ハングアップし、process_exitedコールバックは呼び出されません。Python asyncio:ワーカースレッドでsubprocess_execを実行する

ワーカースレッドでsubprocess_execを実行すると、文書化されていない不具合や問題が発生している可能性があります。バックグラウンドスレッドの実装の実装方法と関係している可能性があります。しかし、それは私だけものを台無しにするかもしれない。

簡単、次のように再現可能な例である:シェルコマンドls -lhを実行し、データをサブプロセスから受信されたときのコールバックをトリガーする

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() 

だからここで、Iセットアップasyncioイベントループ、サブプロセスが終了したときの別のコールバック。

私がPythonプログラムのメインスレッドで直接run_loop()を呼び出すと、すべてうまく行きます。しかし、私は言った場合:

t = threading.Thread(target = run_loop) 
t.start() 
t.join() 

は、次に何が起こるかpipe_data_received()コールバックが正常に起動されますが、process_exited()が呼び出されることはありませんし、プログラムがハングしていることです。

次のように周りのグーグルと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を参照してください。

関連する問題