2017-01-27 2 views
2

Je suis relativement nouveau sur python, mais j'ai pu obtenir un programme raisonnablement utile pour exécuter un grand nombre de données. Je suis capable de l'exécuter séquentiellement sur plusieurs ensembles de données en utilisant un autre script python pour appeler le programme en série, mais je voulais créer une interface graphique et utiliser le multithreading pour permettre aux autres de l'utiliser sans connaître tous les tenants et aboutissants de la programmation. J'ai créé l'interface graphique avec succès, et peut alimenter les données bidirectionnellement en utilisant des signaux et des emplacements. Ce qui me pose problème, c'est de créer plusieurs threads avec la même fonction.Comment verrouiller correctement Qthreads dans pyqt5 en utilisant Python3

J'ai fait quelques recherches et il semble que la fonction doit être threadsafe, et malheureusement la mienne n'est pas parce que j'utilise curve_fit() de scipy, qui n'est pas threadsafe. Donc, basé sur ce que j'ai lu dans ce forum et d'autres, je devrais utiliser mutex.lock(), mais je reçois le "SystemError: null argument à la routine interne" lors de l'appel curve_fit()

Voici quelques exemples Code de démontrer ce que je l'ai fait:

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

Dans un autre dossier, j'ai la fonction principale que je fais appel comme fil conducteur:

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

je peux courir avec succès le seul code appelant un fil, mais si j'appelle plusieurs threads, la fenêtre de sortie affiche 'waypoint2' ch thread appelé, puis se bloque avec l'erreur système que j'ai mentionné ci-dessus.

Qu'est-ce que je fais mal? Dois-je utiliser des processus distincts au lieu de Qthreads? Est-ce que je me méprends sur le fonctionnement des fils? Je veux qu'ils fonctionnent dans des espaces variables indépendants.

Répondre

2

L'utilisation d'un mutex ne rend vraiment un thread sûr que si toutes les autres choses partageant les internes de la fonction respectent aussi le mutex. Dans ce cas, cela ne sera pas le cas, car en utilisant un mutex empêche les appels simultanés à curve_fit, vous ne savez pas ce qui est dangereux pour la fonction, et donc vous ne pouvez pas être sûr que quelque chose d'autre ne sera pas utilisez également le bit de code non sécurisé en même temps dans un autre thread (par exemple le thread principal). Couplé avec le fait que le Python GIL empêche le vrai threading (le thread ne fournit qu'une augmentation de vitesse en Python si votre tâche est liée à l'E/S plutôt que liée au CPU), je suggère de passer à un modèle multiprocessus.