为什么在不同线程中调用asyncio subprocess.communicate会挂起?

时间:2018-12-21 10:21:54

标签: python subprocess python-asyncio

当我必须在asyncio事件循环内运行子流程时,子流程通信挂起,整个情况都在单独的线程内。

我了解到,为了在单独的线程中运行子进程,我需要拥有

<table>
  <tr>
    <th>Header 1</th>
    <th>Header 2</th>
    <th>Header 3</th>
  </tr>
  <tr>
    <td contenteditable="true">data 1</td>
    <td contenteditable="true">data 2</td>
    <td contenteditable="true">data 3</td>
  </tr>
</table>

满足上述条件后,我完成了子流程工作。但是subprocess.communicate现在正在挂起。如果从主线程调用相同的代码,则可以正常工作。

进一步研究之后,我发现由于过程本身并没有完成,因此沟通正在暂停。 1. an event loop running in main thread, and 2. a child watcher must be initiated in main thread. 实际上正在挂起。

当我试图在子流程中发出的命令本身挂起时,我已经看到通讯挂起,但这不是这种情况。

ie await process.wait()

2 个答案:

答案 0 :(得分:0)

看起来子进程SIGCHLD不是由工作线程而是由父线程接收的。这意味着process.wait()不会由操作系统发出信号。还有另一个discussion about this here

儿童观察者似乎应该检测到SIGCHLD并将其传播到其他线程(或pid)及其事件循环,这似乎也是其主要设计目的。 (缺少文档,因此需要阅读源文件。)

注意:我认为t.join()阻塞了运行子观察程序的主线程 ,因此需要进行修复。我只是在其中放了一个while循环,并在t.is_alive()返回False时结束了主事件循环。

我注意到signal_noop正在射击,所以很好。该问题似乎与似乎已正确设置的 signal.set_wakeup_fd (self._csock.fileno())有关。我需要进行更多调试,以了解该事件的处理方式以及主事件循环为何未收到该信号的原因。我注意到了unix_events.py 中的 _process_self_data(自身,数据)没有发生。

Signals and threads

Python信号处理程序始终在Python主线程中执行,即使该信号是在另一个线程中接收到的也是如此。这意味着信号不能用作线程间通信的手段。您可以改为使用来自线程模块的同步原语。

此外,只允许主线程设置新的信号处理程序。

答案 1 :(得分:0)

我认为这可以解决。对线程使用循环run_in_executor。

import asyncio
import shlex
import threading
import subprocess
import logging
async def sendcmd(cmd):
    cmdseq = tuple(shlex.split(cmd))
    print(cmd)
    p = await asyncio.create_subprocess_exec(*cmdseq, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
    print(p.pid)
    output = (await asyncio.wait_for(p.communicate(), 5))[0]
    output = output.decode('utf8')
    print(output)
    return output

async def myfunc(cmd):
    o = await sendcmd(cmd)
    return o

def myfunc2():
    thread_loop = asyncio.new_event_loop()
    asyncio.set_event_loop(thread_loop)
    thread_loop.set_debug(True)     
    tasks = []
    tasks.append(asyncio.ensure_future(myfunc('uname -a')))
    thread_loop.run_until_complete(asyncio.gather(*tasks))
    thread_loop.close()

async def myfunc3(loop=None):
    await loop.run_in_executor(None, myfunc2)    

def main():
    logfilename='test.log'
    print('Writing log to {}'.format(logfilename))
    logging.basicConfig(filename=logfilename, level=logging.INFO, format='%(asctime)s %(name)s %(module)s %(levelname)-8s %(message)s')
    logging.getLogger('asyncio').setLevel(logging.DEBUG)
    root = logging.getLogger(__name__)

    cw=asyncio.get_child_watcher()
    main_loop = asyncio.get_event_loop()
    main_loop.run_until_complete(asyncio.ensure_future(myfunc3(loop=main_loop)))
    cw.close()
    main_loop.close()

main()