python - 当应用程序退出时,使用 Python subprocess 模块启动的进程不会被终止

标签 python subprocess

当我退出应用程序(代码如下)时,我使用 subprocess.Popen 启动的两个 ping 进程不会自动终止,并且仍然显示在 Windows 7 任务列表中。

当应用程序运行时,ping 进程在 Python.exe 下显示为两个线程。当应用程序退出时,这两个进程将移动到系统进程选项卡并继续在那里运行。

我该如何解决这个问题?我希望在我的应用程序关闭时终止这两个 ping 进程。

# -*- coding: utf-8 -*- 

import sys 
import time 
import subprocess 
from threading import Thread 
import re 
from PyQt4.QtGui import QMainWindow, QApplication, QStandardItemModel, QStandardItem, QWidget, QVBoxLayout, QTableView 
from PyQt4.QtCore import pyqtSignature, Qt, QTimer, SIGNAL, QString, QMetaObject 
from Queue import Queue 

try: 
    _fromUtf8 = QString.fromUtf8 
except AttributeError: 
    _fromUtf8 = lambda s: s 

class Ui_MainWindow(object): 
    def setupUi(self, MainWindow): 
        MainWindow.setObjectName(_fromUtf8("MainWindow")) 
        MainWindow.resize(500, 435) 
        self.centralWidget = QWidget(MainWindow) 
        self.centralWidget.setObjectName(_fromUtf8("centralWidget")) 
        self.verticalLayout = QVBoxLayout(self.centralWidget) 
        self.verticalLayout.setObjectName(_fromUtf8("verticalLayout")) 
        self.tableView = QTableView(self.centralWidget) 
        self.tableView.setObjectName(_fromUtf8("tableView")) 
        self.verticalLayout.addWidget(self.tableView) 
        MainWindow.setCentralWidget(self.centralWidget) 

        self.retranslateUi(MainWindow) 
        QMetaObject.connectSlotsByName(MainWindow) 

    def retranslateUi(self, MainWindow): 
        MainWindow.setWindowTitle(QApplication.translate("MainWindow", "Ping Tester", None, QApplication.UnicodeUTF8)) 

if sys.platform.startswith('linux'): 
    getdata = re.compile(r"icmp_req=(\d+) ttl=(\d+) time=([\d\.]+)\sms") 
    pingstr = ["ping", "-n", "-i 0.2"] 
    filtered = "Packet filtered" 
    delaytime = 200 
else: 
    getdata = re.compile(r"=([\d\.]+)ms TTL=(\d+)") 
    pingstr = ["ping.exe", "-t"] 
    timeout = "Request timed out." 
    delaytime = 500 

try: 
    with open("ips.conf", "r") as f: 
        t_node = f.read().decode('utf-8') 
        if not t_node: 
            raise IOError 
except IOError: 
    with open("ips.conf", "w") as f: 
        t_node = u""" 
        8.8.8.8-Google 
        184.22.112.34-USAHE 
        """ 
        f.write(t_node.encode('utf-8')) 

node = [] 
for line in t_node.split('\n'): 
    try: 
        ip, desc = line.strip().split("-") 
        node.append((ip, desc)) 
    except ValueError: 
        pass 
nodecount = len(node) 

class MainWindow(QMainWindow, Ui_MainWindow): 
    """ 
    Class documentation goes here. 
    """ 
    def __init__(self, parent = None): 
        """ 
        Constructor 
        """ 
        QMainWindow.__init__(self, parent) 
        self.setupUi(self) 
        self.model = QStandardItemModel() 
        self.model.setColumnCount(6) 
        self.model.setRowCount(nodecount) 
        self.model.setHorizontalHeaderLabels(["IP", "Description", "Loss%", "CurPing", "AvgPing", "TTL"]) 
        for i, (ip, desc) in enumerate(node): 
            self.setitem(i, 0, ip) 
            self.setitem(i, 1, desc) 
            self.setitem(i, 2, "") 
            self.setitem(i, 3, "") 
            self.setitem(i, 4, "") 
            self.setitem(i, 5, "") 
        self.tableView.setModel(self.model) 
        for i in range(len(node)): 
            self.tableView.setRowHeight(i, 18) 
        self.resizetable() 
        self.timer = QTimer(self) 
        self.connect(self.timer, 
                     SIGNAL("timeout()"), 
                     self.checkitems) 
        self.timer.start(delaytime) 

    def checkitems(self): 
        while not q.empty(): 
            item = q.get() 
            self.chgtxt(*item) 
            q.task_done() 
        self.resizetable() 

    def resizetable(self): 
        self.tableView.resizeColumnsToContents() 

    def chgtxt(self, x, y, value): 
        self.model.item(x, y).setText(value) 

    def setitem(self, x, y, value): 
        self.model.setItem(x, y, QStandardItem(value)) 

app = QApplication(sys.argv) 
ui = MainWindow() 
ui.show() 
q = Queue() 

def pinger(i, ip, desc): 
    s = "" 
    avgping = 0 
    count = 0 
    timeoutcount = 0 
    ret = subprocess.Popen(pingstr + [ip], 
                            stdout=subprocess.PIPE) 
    while True: 
        try: 
            s += ret.stdout.read(1) 

            tryfind = getdata.findall(s) 
            if sys.platform.startswith('linux'): 
                if len(tryfind) > 0: 
                    req, ttl, crtping = tryfind[-1] 
                    avgping += float(crtping) 
                    count += 1 
                    q.put((i, 3, crtping + "ms")) 
                    q.put((i, 4, "%.2f" % (avgping * 1.0 / count) + "ms")) 
                    q.put((i, 5, ttl)) 
                    q.put((i, 2, "%.2f" % ((int(req) - count) * 100.0 / int(req)))) 
                    s = "" 
                elif filtered in s: 
                    q.put((i, 2, "Failed")) 
                    q.put((i, 3, "Failed")) 
                    q.put((i, 4, "Failed")) 
                    q.put((i, 5, "Failed")) 
                    ret.kill() 
                    s = "" 
            else: 
                if len(tryfind) > 0: 
                    crtping, ttl = tryfind[-1] 
                    avgping += float(crtping) 
                    count += 1 
                    q.put((i, 3, crtping + "ms")) 
                    q.put((i, 4, "%.2f" % (avgping * 1.0 / count) + "ms")) 
                    q.put((i, 5, ttl)) 
                    q.put((i, 2, "%.2f" % (timeoutcount * 100.0 / (count + timeoutcount)))) 
                elif timeout in s: 
                    timeoutcount += 1 
                    q.put((i, 2, "-")) 
                    q.put((i, 3, "-")) 
                    if count: 
                        q.put((i, 5, "%.2f" % (timeoutcount * 100.0 / (count + timeoutcount)))) 
                    else: 
                        q.put((i, 5, "-")) 
                    s = "" 
        except IOError: 
            print s 
            break 

def startworkers(): 
    for i, (ip, desc) in enumerate(node): 
        worker = Thread(target=pinger, args=(i, ip, desc)) 
        worker.setDaemon(True) 
        worker.start() 
        time.sleep(delaytime / 10000.0) 

startthread = Thread(target=startworkers) 
startthread.setDaemon(True) 
startthread.start() 

sys.exit(app.exec_())  

最佳答案

这是一种使用 atexit 的方法:

import subprocess
from threading import Thread
import sys 
import atexit

from PyQt4.QtGui import QMainWindow, QApplication

class Ui_MainWindow(object): 
    def setupUi(self, MainWindow): 
        MainWindow.setObjectName(("MainWindow")) 
        MainWindow.resize(500, 435) 

def runproc():
    p = subprocess.Popen(["sleep", "500"])
    atexit.register(kill_proc, p)
    p.communicate()

def kill_proc(proc):
    try:
        proc.terminate()
    except Exception:
        pass

class MainWindow(QMainWindow):
    def __init__(self):
        QMainWindow.__init__(self)
        self.resize(300, 300)

if __name__ == "__main__":
    app = QApplication(sys.argv)
    ui = MainWindow()
    ui.show()
    for i in range(0, 3): 
        t = Thread(target=runproc)
        t.start()
    sys.exit(app.exec_())

每个线程都会注册一个 atexit 回调,该回调传递它创建的 Popen 对象。当进程通过正常方式退出时,atexit 处理程序将被调用,并且在每个处理程序中,我们都会对 Popen 对象调用 terminate,从而终止该进程。请注意,这不会处理有人向您的进程发送 SIGKILL 之类的信号;它仅通过关闭 QMainWindow 来退出,或者通过 CLI 运行时执行 Ctrl+C 之类的操作。

编辑:

要处理关闭时出现的异常,您必须更改代码处理从子进程的 stdout 读取的数据的方式。当您在关闭时终止子进程时,它们会将 None 发送到其 stdout,并且您的线程会尝试处理该 None,就好像它是实际数据一样。您只需要优雅地处理这种情况即可:

out = ret.stdout.read(1)
if not out:
    break
s += out 
print s
tryfind = getdata.findall(s)

关于python - 当应用程序退出时,使用 Python subprocess 模块启动的进程不会被终止,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/24410518/

相关文章:

python - 如何在SqlAlchemy orm中设置主键自增

scala - 通过python脚本在命令行中执行sbt并输出到文件

python - subprocess.py 返回文件未找到错误

python - 从 python 脚本启动 NGINX

python - 难以通过 python 使用 qhull 管道

python - 我如何在 QMake 中包含 python.h

python - Numpy 删除子矩阵

python - 使用 pkg_resources 时以通用换行模式打开文件?

python - Jython subprocess.call() 到 Python

python - 在对象上 pickle lru_cached 函数