当终止使用 python 子进程 Popen 启动的进程时,如何关闭标准输出管道?
-
06-07-2019 - |
题
我想知道当杀死在不同线程中启动的子进程时是否可以关闭通信管道。如果我不调用communicate(),那么kill()将按预期工作,在一秒而不是五秒后终止进程。
我发现了类似问题的讨论 这里, ,但我没有得到真正的答案。我假设我必须能够关闭管道或显式终止子子进程(在示例中为“睡眠”)并终止它以解锁管道。
我也试图在SO上找到她的答案,但我只找到了 这 和 这 和 这, ,据我所知,这并不能直接解决这个问题(?)。
因此,我想做的事情是能够在第二个线程中运行命令并获取其所有输出,但能够在我需要时立即终止它。我可以通过一个文件并尾部该文件或类似的文件,但我认为应该有更好的方法来做到这一点?
import subprocess, time
from threading import Thread
process = None
def executeCommand(command, runCommand):
Thread(target=runCommand, args=(command,)).start()
def runCommand(command):
global process
args = command.strip().split()
process = subprocess.Popen(args, shell=False, stdout=subprocess.PIPE)
for line in process.communicate():
if line:
print "process:", line,
if __name__ == '__main__':
executeCommand("./ascript.sh", runCommand)
time.sleep(1)
process.kill()
这是脚本:
#!/bin/bash
echo "sleeping five"
sleep 5
echo "slept five"
输出
$ time python poc.py
process: sleeping five
real 0m5.053s
user 0m0.044s
sys 0m0.000s
解决方案
我认为问题在于 process.kill() 只杀死直接子进程(bash),而不是 bash 脚本的子进程。
问题和解决方案描述如下:
- http://www.doughellmann.com/PyMOTW/subprocess/#process-groups-sessions
- 如何终止使用 shell=True 启动的 python 子进程
使用 Popen(..., preexec_fn=os.setsid) 创建进程组,使用 os.pgkill 杀死整个进程组。例如
import os
import signal
import subprocess
import time
from threading import Thread
process = None
def executeCommand(command, runCommand):
Thread(target=runCommand, args=(command,)).start()
def runCommand(command):
global process
args = command.strip().split()
process = subprocess.Popen(
args, shell=False, stdout=subprocess.PIPE, preexec_fn=os.setsid)
for line in process.communicate():
if line:
print "process:", line,
if __name__ == '__main__':
executeCommand("./ascript.sh", runCommand)
time.sleep(1)
os.killpg(process.pid, signal.SIGKILL)
$ time python poc.py
process: sleeping five
real 0m1.051s
user 0m0.032s
sys 0m0.020s
其他提示
在我看来,最容易做到这一点并避免多线程问题的方法是从主线程设置kill标志,并在通信之前在脚本运行线程中检查它,在执行时杀死脚本标志是 True
。
看起来你可能是Python超粗粒度并发的受害者。将脚本更改为:
#!/bin/bash
echo "sleeping five"
sleep 5
echo "sleeping five again"
sleep 5
echo "slept five"
然后输出变为:
process: sleeping five
real 0m5.134s
user 0m0.000s
sys 0m0.010s
如果整个脚本运行,则时间为10秒。所以看起来python控制线程直到bash脚本休眠后才真正运行。同样,如果您将脚本更改为:
#!/bin/bash
echo "sleeping five"
sleep 1
sleep 1
sleep 1
sleep 1
sleep 1
echo "slept five"
然后输出变为:
process: sleeping five
real 0m1.150s
user 0m0.010s
sys 0m0.020s
简而言之,您的代码按逻辑实现。 :)
不隶属于 StackOverflow