How to correctly lock Qthreads in pyqt5 using Python3

…衆ロ難τιáo~ 提交于 2021-02-10 06:29:09

问题


I am relatively new to python, but was able to get a reasonably useful program to run to crunch a lot of data. I am able to run it over multiple sets of data sequentially using another python script to call the program serially, but I wanted to create a GUI and use multithreading to allow others to use it without knowing all the ins and outs of programming. I created the GUI successfully, and can feed data bidirectionally using signals and slots. What I am having trouble with is creating multiple threads with the same function.

I have done some research and it appears that the function needs to be threadsafe, and unfortunately mine is not because I am using curve_fit() from scipy, which is not threadsafe. So, based on what I have read in this forum and others, I should be using mutex.lock(), but I get the "SystemError: null argument to internal routine" when calling curve_fit()

Here is some sample code to demonstrate what I have done:

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

In another file, I have the main function that I am calling as a thread:

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

I can successfully run the code only calling one thread, but if I call multiple threads, then the output window prints out 'waypoint2' for each thread called, then crashes with the system error I mentioned above.

What am I doing wrong? Do I need to use separate processes instead of Qthreads? Am I misunderstanding how threads work? I want them to operate in independent variable spaces.


回答1:


Using a mutex really only makes something thread safe if all of the other things sharing the internals of the function also respects the mutex. In this case, it won't because, while using a mutex prevents simultaneous calls to curve_fit, you don't know what it is that is thread unsafe about the function, and so you can't be sure that something else won't also use the thread unsafe bit of code at the same time in another thread (e.g. the main thread).

Coupled with the fact that the Python GIL prevents true threading (threading only provides a speed boost in Python if your task is IO bound rather than CPU bound), I would suggest moving to a multiprocess model.



来源:https://stackoverflow.com/questions/41904263/how-to-correctly-lock-qthreads-in-pyqt5-using-python3

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!