为了账号安全,请及时绑定邮箱和手机立即绑定

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

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

猛跑小猪 2023-08-22 10:24:49
我正在构建一个运行神经网络的 GUI 应用程序。启动它们效果很好,但我需要能够在网络已经运行和工作的情况下中止计算。我构建了一个小型原型,它向您展示了我的应用程序中问题的主要机制。建筑学窗口类别:包含所有 GUI 元素,是启动和控制一切的主要对象。它包含 aQThreadPool self.__pool和QRunnableObject 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(). 我正在考虑以某种方式将一个对象传递到神经网络的回调参数中,以便中止那里的处理,但我不太确定这是否是正确的方法。
查看完整描述

1 回答

?
白衣非少年

TA贡献1155条经验 获得超0个赞

好吧,现在我已经知道问题出在哪里了。当我终止进程时self.__net.terminate()仍然存在僵尸进程,为什么我认为这不是正确的解决方案。


僵尸进程是由 QRunnable 引起的,它不会终止,因为它正在等待子进程在队列中发送信号。


因此,我需要一个自定义的“终止”函数,它调用 Terminate() 并向 QRunnable 发送一个信号,表明该过程已完成。这样,所有进程和线程都会相应终止,并且不会留下任何僵尸进程。


import tensorflow as tf

from sys             import exit, argv

from multiprocessing import Process, Queue

from PyQt5.QtWidgets import QPushButton, QApplication, QHBoxLayout, QWidget, QLabel

from PyQt5.QtCore    import QRunnable, QObject, pyqtSignal, QThreadPool



class 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())



查看完整回答
反对 回复 2023-08-22
  • 1 回答
  • 0 关注
  • 56 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信