使用多处理,从 PyQt5 应用程序停止子进程,其中子进程不使用事件循环

我正在构建一个运行神经网络的 GUI 应用程序。启动它们效果很好,但我需要能够在网络已经运行和工作的情况下中止计算。

我构建了一个小型原型,它向您展示了我的应用程序中问题的主要机制。

建筑学

窗口类别:

包含所有 GUI 元素,是启动和控制一切的主要对象。它包含 aQThreadPool self.__poolQRunnableObject self.__runner。该QRunnable对象包含神经网络所需的一切。我使用 QRunnable 对象的原因是,在另一个线程中处理神经网络时不阻塞 GUI。我还需要在神经网络和我的应用程序之间进行通信。

运行程序类:
运行程序类处理主窗口和神经网络本身之间的通信。神经网络是一个Process来自多处理的对象,放入self.__net. 为了进行通信,我使用Queue self.__queue. 当 QRunnable 对象启动时,进程以 开始self.__net.start()。我用无限循环观察队列。循环在某些信号上终止。在此示例中,我仅使用 Signal NetSignal.finished。当网络完成后,我向 Window 对象发送一个信号。

RunnerSignal类:
由于QRunnables无法使用信号,因此需要该类将一些信号封装到QRuannaable对象中。

Mnist 类:
Mnist 类是子进程本身,继承自Process. 在这个例子中,它运行了一个非常简单的例子,在神经网络中处理 mnist 数据集。进程周围没有循环,通常可用于停止此类子进程。当神经网络完成时,队列向 QRunnable 对象发送一个信号,以通知进程已完成计算,从而向主窗口发送一个信号。

问题

我需要能够以某种方式停止这个过程。我想尝试用 来杀死它os.kill,但这在我的应用程序中效果不佳。我也尝试过self.__net.terminate()self.__net.kill(). 我正在考虑以某种方式将一个对象传递到神经网络的回调参数中,以便中止那里的处理,但我不太确定这是否是正确的方法。


猛跑小猪
浏览 83回答 1
1回答

白衣非少年

好吧,现在我已经知道问题出在哪里了。当我终止进程时self.__net.terminate()仍然存在僵尸进程,为什么我认为这不是正确的解决方案。僵尸进程是由 QRunnable 引起的,它不会终止,因为它正在等待子进程在队列中发送信号。因此,我需要一个自定义的“终止”函数,它调用 Terminate() 并向 QRunnable 发送一个信号,表明该过程已完成。这样,所有进程和线程都会相应终止,并且不会留下任何僵尸进程。import tensorflow as tffrom sys             import exit, argvfrom multiprocessing import Process, Queuefrom PyQt5.QtWidgets import QPushButton, QApplication, QHBoxLayout, QWidget, QLabelfrom PyQt5.QtCore    import QRunnable, QObject, pyqtSignal, QThreadPoolclass Window(QWidget):    def __init__(self):        QWidget.__init__(self)        self.__btn_run = QPushButton("Start")        self.__btn_stp = QPushButton("Stop")        self.__label   = QLabel("Idle")        self.__runner  = Runner()        self.__pool    = QThreadPool.globalInstance()        self.__btn_run.clicked.connect(self.__run_net)        self.__btn_stp.clicked.connect(self.__stp_net)        self.__runner.signals.finished.connect(self.__on_finished)        self.__btn_stp.setDisabled(True)        self.setLayout(QHBoxLayout())        self.layout().addWidget(self.__btn_run)        self.layout().addWidget(self.__btn_stp)        self.layout().addWidget(self.__label)    def __run_net(self):        self.__btn_run.setDisabled(True)        self.__btn_stp.setEnabled(True)        self.__label.setText("Running")        self.__pool.start(self.__runner)    def __stp_net(self):        self.__runner.close()        # What to do here?    def __on_finished(self):        self.__btn_run.setEnabled(True)        self.__btn_stp.setDisabled(True)        self.__label.setText("Finished")        self.__runner = Runner()class Runner(QRunnable):    def __init__(self):        QRunnable.__init__(self)        self.__queue = Queue()        self.__net   = Mnist(self.__queue)        self.signals = RunnerSignal()    def run(self):        self.__net.start()        while True:            data = self.__queue.get()            if data == NetSignal.finished:                self.signals.finished.emit()                break    def close(self):        self.__net.end_process()class RunnerSignal(QObject):    finished = pyqtSignal()class Mnist(Process):    def __init__(self, queue: Queue):        Process.__init__(self)        self.__queue = queue    def run(self):        mnist = tf.keras.datasets.mnist  # 28x28 Bilder hangeschriebener Ziffern von 0-9        (x_train, y_train), (x_test, y_test) = mnist.load_data()        x_train = tf.keras.utils.normalize(x_train, axis=1)        model   = tf.keras.models.Sequential()        model.add(tf.keras.layers.Flatten())        model.add(tf.keras.layers.Dense(128, activation=tf.nn.relu))        model.add(tf.keras.layers.Dense(128, activation=tf.nn.relu))        model.add(tf.keras.layers.Dense(10, activation=tf.nn.softmax))        model.compile(optimizer="adam",                      loss="sparse_categorical_crossentropy",                      metrics=['accuracy'])        model.fit(x_train, y_train, epochs=8)        self.__queue.put(NetSignal.finished)        self.__queue.close()    def end_process(self):        self.terminate()        self.__queue.put(NetSignal.finished)        self.__queue.close()        class NetSignal:    finished = "finished"if __name__ == "__main__":    main_thread = QApplication(argv)    main_window = Window()    main_window.show()    exit(main_thread.exec())
打开App,查看更多内容
随时随地看视频慕课网APP

相关分类

Python