首页 > 解决方案 > 如何在 Process() 中中断 Queue.get()?

问题描述

在目标函数中实现队列时,我无法停止进程run_event()。队列需要从目标函数返回一个值。该过程可以在没有队列的情况下停止。

from PyQt5.QtWidgets import QHBoxLayout, QApplication, QWidget, QPushButton
from PyQt5.QtCore import QThread
from multiprocessing import Process, Queue,

def run_event(params, queue):
    """Run some task that cant be stopped by polling
       returns a dataframe
    """
    out = arbritrarySolver(params) # Runs a solver that returns a dataframe
    queue.put(out)


class Worker(QObject):
    def __init__(self, params):
        super().__init__()
        self.params = params

    def stop_work(self):
        self.process.terminate()

    def start_work(self):
        self.queue = Queue()
        self.process = Process(target=run_event, args=(self.params, self.queue))
        self.process.start()
        output = self.queue.get()  # output of run_event

class Gui(QWidget):
    def __init__(self):
        super().__init__()
        self.initUI()

    def start_event(self):
        self.worker_thread = QThread()
        self.obj = worker.Worker(params)
        self.obj.moveToThread(self.worker_thread)
        self.worker_thread.started.connect(self.obj.start_work)
        self.stopButton.clicked.connect(self.obj.stop_work)
        self.worker_thread.start()

    def initUI(self):
        self.runButton = QPushButton('Run', self)
        self.runButton.clicked.connect(self.start_event)
        hbox = QHBoxLayout()
        hbox.addWidget(self.runButton)
        self.setLayout(hbox)
        self.show()

if __name__ == "__main__":
    app = QApplication(sys.argv)
    ex = Gui()
    sys.exit(app.exec_())

我如何实现run_event()在实现队列时使用停止按钮中断的解决方案?

标签: pythonpython-3.xpyqtmultiprocessingpyqt5

解决方案


问题是,当您使用 self.queue.get() 时,您正在阻塞工作人员所在的辅助线程,而不是可能的策略是使用计时器来检查队列的状态,并相应地发送信号到带有结果的 GUI。

from functools import partial
import multiprocessing as mp
from PyQt5 import QtCore, QtWidgets

from foo_package import arbritrarySolver


def run_event(params, queue):
    """Run some task that cant be stopped by polling
       returns a dataframe
    """
    out = arbritrarySolver(params)  # Runs a solver that returns a dataframe
    queue.put(out)


class Worker(QtCore.QObject):
    resultChanged = QtCore.pyqtSignal(object)
    finished = QtCore.pyqtSignal()

    def __init__(self):
        super().__init__()
        self.m_timer = QtCore.QTimer(self)
        self.m_timer.timeout.connect(self.check_result)
        self.process = None

    @QtCore.pyqtSlot()
    def stop_work(self):
        if isinstance(self.process, mp.Process):
            self.process.terminate()

    @QtCore.pyqtSlot(object)
    def start_work(self, params):
        self.queue = mp.Queue()
        self.process = mp.Process(target=run_event, args=(params, self.queue))
        self.process.start()
        self.m_timer.start(0)

    @QtCore.pyqtSlot()
    def check_result(self):
        if not self.process.is_alive():
            self.finished.emit()
            self.m_timer.stop()

        if not self.queue.empty():
            r = self.queue.get(block=False)
            self.resultChanged.emit(r)


class Gui(QtWidgets.QWidget):
    def __init__(self):
        super().__init__()

        self.worker_thread = QtCore.QThread(self)
        self.worker_thread.start()

        self.obj = Worker()
        self.obj.moveToThread(self.worker_thread)
        self.obj.resultChanged.connect(self.onResultChanged)
        self.obj.finished.connect(self.onFinished)

        self.initUI()

    @QtCore.pyqtSlot()
    def start_event(self):
        params = "aaa", "bbb"
        wrapper = partial(self.obj.start_work, params)
        QtCore.QTimer.singleShot(0, wrapper)
        self.runButton.setDisabled(True)
        self.stopButton.setDisabled(False)

    @QtCore.pyqtSlot()
    def onFinished(self):
        self.runButton.setDisabled(False)
        self.stopButton.setDisabled(True)

    @QtCore.pyqtSlot(object)
    def onResultChanged(self, result):
        print(result)

    def initUI(self):
        self.stopButton = QtWidgets.QPushButton("Stop")
        self.stopButton.setDisabled(True)
        self.runButton = QtWidgets.QPushButton("Run")
        self.runButton.clicked.connect(self.start_event)
        self.stopButton.clicked.connect(self.obj.stop_work)
        hbox = QtWidgets.QHBoxLayout(self)
        hbox.addWidget(self.runButton)
        hbox.addWidget(self.stopButton)
        self.show()

    def closeEvent(self, event):
        self.obj.stop_work()
        super().closeEvent(event)


if __name__ == "__main__":
    import sys

    app = QtWidgets.QApplication(sys.argv)
    ex = Gui()
    sys.exit(app.exec_())

推荐阅读