python - 为什么在不同线程中调用 asyncio subprocess.communicate 会挂起?
问题描述
当我必须在 asyncio 事件循环中运行子进程时,我遇到子进程通信挂起的情况,并且整个事情都在一个单独的线程中。
我了解到,为了在单独的线程中运行子进程,我需要
1. an event loop running in main thread, and
2. a child watcher must be initiated in main thread.
在具备上述条件后,我得到了我的子流程工作。但是 subprocess.communicate 现在挂了。如果从主线程调用它,则相同的代码正在工作。
进一步挖掘后,我观察到通信挂起,因为该过程没有自行完成。ie await process.wait()
实际上是挂着的。
当我试图在子进程本身中发出的命令挂起时,我已经看到通信挂起,但这里不是这种情况。
import asyncio
import shlex
import threading
import subprocess
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():
loop = asyncio.new_event_loop()
asyncio.set_event_loop(loop)
tasks = []
tasks.append(asyncio.ensure_future(myfunc('uname -a')))
loop.run_until_complete(asyncio.gather(*tasks))
async def myfunc3():
t = threading.Thread(target=myfunc2)
t.start()
t.join()
def main():
asyncio.get_child_watcher()
loop = asyncio.get_event_loop()
loop.run_until_complete(asyncio.ensure_future(myfunc3()))
loop.close()
main()
解决方案
看起来子进程 SIGCHLD 没有被工作线程接收,而是被父进程接收。这些意味着 process.wait() 不会由操作系统发出信号。这里有另一个关于这个的讨论。
看起来孩子观察者应该检测到 SIGCHLD 并将其传播到其他线程(或 pid)及其事件循环,这似乎也是它的主要设计目的。(缺少文档,因此需要阅读源代码。)
注意:我认为 t.join() 阻塞了运行子观察者的主线程,因此需要修复。我只是在那里放了一个 while 循环,并在 t.is_alive() 返回 False 时结束主事件循环。
我注意到signal_noop正在触发,这很好。该问题似乎与 似乎设置正确的signal.set_wakeup_fd (self._csock.fileno()) 有关。我需要进行更多调试以了解该事件是如何处理的,以及为什么主事件循环没有收到该信号。我注意到 unix_events.py 中的_process_self_data(self, data)没有发生。
Python 信号处理程序始终在 Python 主线程中执行,即使信号是在另一个线程中接收到的。这意味着信号不能用作线程间通信的手段。您可以改用 threading 模块中的同步原语。
此外,只允许主线程设置新的信号处理程序。
推荐阅读
- vb.net - 如何在从 app.config 检索的 FolderBrowser 中设置默认路径?
- azure - CosmosDB 中的分区键与文档 ID
- node.js - 如何在 AWS EC2 上部署 Node.js Express 服务器 + Vue.js 应用程序
- java - 如何获取合并报表的总页数
- vba - 如何使用查询结果填充 ComboBox
- android - 任务':app:processDebugManifest'的执行失败 - React Native
- vb.net - 可以获得文件夹的最后修改日期吗?
- sql - 如何判断 SQL Server 的情况?
- python - 函数未在 spyder 中列出
- vb.net - 防止 Rest API 中的重复