当app退出时,不会终止使用Python子进程模块启动的进程

时间:2014-06-25 13:48:01

标签: 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_())  

2 个答案:

答案 0 :(得分:1)

使用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处理程序,并在每个处理程序中调用terminate对象上的Popen,这会终止进程。请注意,这不会处理向您的进程发送SIGKILL信号的人;如果您通过CLI运行,它只会通过关闭QMainWindow或者执行类似Ctrl + C的操作来处理它。

修改

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

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

答案 1 :(得分:0)

您有一般设计问题:

  • 你在守护程序线程中启动子进程,而不必在程序结束时明确地停止它们
  • 但您希望流程停止

恕我直言,在你的情况下,最简单(和最干净)的解决方案是明确地要求你的线程停止并让他们杀死启动的子进程:

  • 设置全局变量global stopping = False
  • 使用非守护程序线程
  • 替换最后的sys.exit(app.exec_())
    cr = app_exec_()
    stopping = true
    sys.exit(cr)
    
  • 在pinger中,将while True:替换为

    global stopping
    while True:
        if stopping:
            ret.kill()
            break
    

这足以正确杀死你的子进程。