【问题标题】:How to apply the multiprocessing module on my PyQt-GUI如何在我的 PyQt-GUI 上应用多处理模块
【发布时间】:2019-10-07 16:13:26
【问题描述】:

我知道这是一个老问题,并且已经在其他问题中找到了答案,例如此线程 here。但是,我在应用它时遇到了一些问题。

我现在的方式如下:我有我的 MainWindow 类,我可以在其中输入一些数据。然后我有一个 Worker 类,它是一个 PySide2.QtCore.QThread 对象。我将一些来自 MainWindow 的输入数据传递给这个类。在这个 Worker 类中,我有一个设置一些 ODE 的方法,在 Worker 类的另一个方法中,这些 ODE 由 scipy.integrate.solve_ivp 解决。集成完成后,我通过信号将结果发送回主窗口。所以代码大致是这样的:

import PySide2
from scipy.integrate import solve_ivp

class Worker(QtCore.QThread):

    def __init__(self,*args,**kwargs):
        super(Worker,self).__init__()
        "Here I collect input parameters"

    def run(self):
        "Here I call solve_ivp for the integration and send a signal with the 
        solution when it is done"

    def ode_fun(self,t,c):
        "Function where the ode equations are set up"


class Ui_MainWindow(QtWidgets.QMainWindow):
    def __init__(self):
        "set up the GUI"
        self.btnStartSimulation.clicked.connect(self.start_simulation) #button to start the integration

    def start_simulation(self):
        self.watchthread(Worker)
        self.thread.start()

    def watchthread(self,worker):
        self.thread = worker("input values")
        "connect to signals from the thread"

现在我明白了,使用多处理模块我应该能够在另一个处理器内核上运行集成的线程,以使其更快并减少 GUI 的滞后。但是,从上面的链接中,我不确定应该如何应用这个模块,甚至不确定如何重构我的代码。我是否必须将我现在在我的 Worker 类中拥有的代码放入另一个类中,或者我是否能够以某种方式将多处理模块应用到我现有的线程上? 非常感谢任何帮助!

编辑: 新代码如下所示:

class Worker(QtCore.QThread):

    def __init__(self,*args,**kwargs):
        super(Worker,self).__init__()
        self.operation_parameters = args[0]
        self.growth_parameters = args[1]
        self.osmolality_parameters = args[2]
        self.controller_parameters = args[3]
        self.c_zero = args[4]

    def run(self):
        data = multiprocessing.Queue()
        input_dict = {"function": self.ode_fun_vrabel_rushton_scaba_cont_co2_oxygen_biomass_metabol,
                      "time": [0, self.t_final],
                      "initial values": self.c_zero}
        data.put(input_dict)
        self.ode_process = multiprocessing.Process(target=self.multi_process_function, args=(data,))
        self.ode_process.start()

        self.solution = data.get()

    def multi_process_function(self,data):
        self.message_signal = True
        input_dict = data.get()
        solution = solve_ivp(input_dict["function"], input_dict["time"],
                      input_dict["initial values"], method="BDF")
        data.put(solution)

    def ode_fun(self,t,c):
        "Function where the ode equations are set up"
        (...) = self.operation_parameters
        (...) = self.growth_parameters
        (...) = self.osmolality_parameters
        (...) = self.controller_parameters

如果我通过self.“parameter_name”访问ode_fun函数中的参数可以吗?还是我还必须将它们与数据参数一起传递?
使用当前代码,我收到以下错误:TypeError: can't pickle Worker objects

【问题讨论】:

    标签: python python-3.x multithreading pyqt multiprocessing


    【解决方案1】:

    你可以像这样从你的工作人员那里调用它:

    import PySide2
    from scipy.integrate import solve_ivp
    import multiprocessing
    
    class Worker(QtCore.QThread):
    
        def __init__(self,*args,**kwargs):
            super(Worker, self).__init__()
            self.ode_process = None
    
            "Here I collect input parameters"
    
        def run(self):
            "Here I call solve_ivp for the integration and send a signal with the solution when it is done"
            data = multiprocessing.Queue()
            data.put("all objects needed in the process, i would suggest a single dict from which you extract all data")
            self.ode_process = multiprocessing.Process(target="your heavy duty function", args=(data,))
            self.ode_process.start()  # this is non blocking
    
            # if you want it to block:
            self.ode_process.join()
            # make sure you remove all input data from the queue and fill it with the result, then to get it back:
            results = data.get()
    
            print(results)  # or do with it what you want to do...
    
        def ode_fun(self, t, c):
            "Function where the ode equations are set up"
    
    
    class Ui_MainWindow(QtWidgets.QMainWindow):
        def __init__(self):
            "set up the GUI"
            self.btnStartSimulation.clicked.connect(self.start_simulation) #button to start the integration
    
        def start_simulation(self):
            self.watchthread(Worker)
            self.thread.start()
    
        def watchthread(self,worker):
            self.thread = worker("input values")
            "connect to signals from the thread"
    

    另外请注意,您现在每次按下开始模拟时都会覆盖正在运行的进程。您可能想为此使用某种锁。

    【讨论】:

    • 感谢您的帮助!看看我对当前代码的编辑。我想启动进程时检索到错误:“TypeError: can't pickle Worker objects”
    • 嗯,如果您尝试将 multi_process_function 移到您的班级之外怎么办,在我看来,它不需要任何来自自己的东西?您的 Worker 类中似乎有些东西无法腌制并发送到其他进程。 self.message_signal 也可以在 run() 函数中设置吧?
    • 我认为问题在于我想通过的 ode_fun。根据这个线程(link),我想我还必须将我的 ode_fun 移出课堂,而不是通过 dict 传递它。你怎么看?
    • 绝对值得一试!有了这些东西,我倾向于测试到底哪里出了问题,尝试到底是什么对象导致了这个错误,然后看看如何将它移动到不会导致问题的地方。
    猜你喜欢
    • 1970-01-01
    • 2019-09-13
    • 1970-01-01
    • 2017-05-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多