Skip to main content
 首页 » 编程设计

python之如何使用Python3在pyqt5中正确锁定Qthreads

2024年10月01日95pander-it

我是 python 的新手,但能够获得一个相当有用的程序来运行以处理大量数据。我能够使用另一个 python 脚本按顺序在多组数据上运行它以串行调用程序,但我想创建一个 GUI 并使用多线程以允许其他人在不知道编程的所有细节的情况下使用它。我成功地创建了 GUI,并且可以使用信号和槽双向提供数据。我遇到的问题是创建具有相同功能的多个线程。

我做了一些研究,看来该函数需要是线程安全的,不幸的是我的不是,因为我使用的是 scipy 中的 curve_fit(),它不是线程安全的。因此,根据我在本论坛和其他论坛中阅读的内容,我应该使用 mutex.lock(),但在调用 curve_fit() 时出现“SystemError: null argument to internal routine”

下面是一些示例代码来演示我所做的:

    import sip 
    sip.setapi('QString', 2) 
 
    import sys, time 
    from PyQt5 import QtCore, QtGui, uic, QtWidgets 
    from ZthCalculation import ZthObject 
 
    qtCreatorFile = "PyQtZthUI_01.ui" # Enter file here. 
 
    Ui_MainWindow, QtBaseClass = uic.loadUiType(qtCreatorFile) 
 
    #class MyApp(QtGui.QMainWindow, Ui_MainWindow): 
    class MyApp(QtWidgets.QMainWindow, Ui_MainWindow): 
        def __init__(self): 
            super(self.__class__, self).__init__() 
            QtWidgets.QMainWindow.__init__(self) 
            Ui_MainWindow.__init__(self) 
            self.setupUi(self) 
            self.RunButton.clicked.connect(self.RunZthTest) 
    . 
    . 
    . 
        def RunZthTest(self): 
  #create as processes instead of threads??? 
  #            self.Process1 = QtCore.QProcess() 
            self.Thread1 = QtCore.QThread() 
            self.obj1 = ZthObject(self.InputWet1.text(), self.InputDry1.text(), self.Output1.text(), self.side1) 
            self.obj1.moveToThread(self.Thread1) 
            self.Thread1.started.connect(self.obj1.ZthCalculation) 
            self.obj1.textBox.connect(self.updateTextBox1) 
            self.signal1 = self.obj1.finished.connect(self.Thread1.quit) 
            self.Thread1.setObjectName("Thread1") 
            self.Thread1.start() 
            time.sleep(.1) 
 
            self.Thread2 = QtCore.QThread() 
            self.obj2 = ZthObject(self.InputWet2.text(), self.InputDry2.text(), self.Output2.text(), self.side2) 
            self.obj2.moveToThread(self.Thread2) 
            self.Thread2.started.connect(self.obj2.ZthCalculation) 
            self.obj2.textBox.connect(self.updateTextBox2) 
            self.signal2 = self.obj2.finished.connect(self.Thread2.quit) 
            self.Thread2.setObjectName("Thread2") 
            self.Thread2.start() 
            time.sleep(.1) 
 
            self.Thread3 = QtCore.QThread() 
            self.obj3 = ZthObject(self.InputWet3.text(), self.InputDry3.text(), self.Output3.text(), self.side3) 
            self.obj3.moveToThread(self.Thread3) 
            self.Thread3.started.connect(self.obj3.ZthCalculation) 
            self.obj3.textBox.connect(self.updateTextBox3) 
            self.signal3 = self.obj3.finished.connect(self.Thread3.quit) 
            self.Thread3.setObjectName("Thread3") 
            self.Thread3.start() 
    . 
    . 
    . 
 
    if __name__ == "__main__": 
        app = QtWidgets.QApplication(sys.argv) 
        window = MyApp() 
        window.show() 
    #    sys.exit(app.exec_()) 
        app.exec_() 

在另一个文件中,我将主函数作为线程调用:

class ZthObject(QtCore.QObject): 
    killthread = False 
    finished = QtCore.pyqtSignal() 
    textBox = QtCore.pyqtSignal(str) 
    def __init__(self, wetFilePath, dryFilePath, outFilePath, side, parent=None): 
        super(self.__class__, self).__init__() 
        self.wetFilePath = wetFilePath 
        self.dryFilePath = dryFilePath 
        self.outFilePath = outFilePath 
        self.side = side 
        self.mutex = QtCore.QMutex() 
    def cleanup(self): 
        ZthObject.killthread = True 
 
#    def ZthCalculation(self, wetFilePath, dryFilePath, outFilePath, side): 
    def ZthCalculation(self): 
        #calculations here 
. 
. 
. 
        print("waypoint2") 
        self.mutex.lock() 
        popt, pcov = curve_fit(Foster6, timeShort, ZthjcShort, p0 = [Rs, taus]) 
        self.mutex.unlock() 
. 
. 
. 
        self.finished.emit() 

我可以成功运行只调用一个线程的代码,但如果我调用多个线程,那么输出窗口会为每个调用的线程打印出“waypoint2”,然后因我上面提到的系统错误而崩溃。

我做错了什么?我需要使用单独的进程而不是 Qthreads 吗?我误解了线程是如何工作的吗?我希望它们在独立的变量空间中运行。

请您参考如下方法:

如果共享函数内部的所有其他事物也尊重互斥锁,则使用互斥锁实际上只会使某些线程安全。在这种情况下,它不会是因为,虽然使用互斥锁可以防止同时调用 curve_fit,但您不知道该函数的线程不安全是什么,因此您不能确保其他东西不会同时在另一个线程(例如主线程)中使用线程不安全代码位。

再加上 Python GIL 阻止了真正的线程(如果您的任务是 IO 绑定(bind)而不是 CPU 绑定(bind),线程仅在 Python 中提供速度提升),我建议转向多进程模型。