I’m trying to develop a software with PyQt, but I often get stuck on software crashes without debug information (only the exit code 0xC0000409). I’m using QThread, and I wrote a system like this:
class serialThreadC(QThread): updateOutBox = QtCore.pyqtSignal(str) updateStatus = QtCore.pyqtSignal(int) def __init__(self): super(serialThreadC, self).__init__() self.ser = False self.state = 0 self.serialEnabled = False def run(self): while True: if self.state == -3 or self.state == -2: if self.SerialEnabled: self.updatePB(20) elif self.state == 0: if self.serialEnabled: self.updatePB(20) def ConnDisconn(self): self.serialEnabled = not self.serialEnabled def updatePB(self, stat): self.state = stat self.updateStatus.emit(self.state) serialThread = serialThreadC() serialThread.start() ## sw is a QDialog already loaded serialThread.updateOutBox.connect(sw.updateOutBox) serialThread.updateStatus.connect(sw.updateStatus) sw.PB_ConnDisconn.clicked.connect(serialThread.ConnDisconn)
I have crashes when I read/write serialEnabled
in run()
or in ConnDisconn()
. I know that PyQt is not thread-safe and that a wrong handling of variables gives crashes of my type, but I can’t understand what is wrong with my code. My idea (maybe wrong) is that all serialThread
methods are executed on the same thread, also if they are connected to a gui (main thread). Is that wrong? In the same way, I emit events from serialThread
and I connected them to the GUI, but that never gave me problems.
Can you see the mistake I made? Is there a way to debug the code if there is a crash without other infos? (I use PyCharm 2017.1.3).
Advertisement
Answer
PyQt is thread-safe to the same extent that Qt is thread-safe. The Qt docs will tell you which parts of their API are guaranteed to be so, and under what circumstances.
Cross-thread signals are thread-safe, so calling the updatePB
method in your example is okay. Your ConnDisconn
method is not thread-safe, but that has got nothing to do with PyQt or Qt – it’s just a consequence of how you wrote it. The serialEnabled
attribute could be read/written by two threads simultaneously, so the behaviour is strictly undefined. A thread-safe way of writing this would be to use a mutex, like so:
class serialThreadC(QThread): updateOutBox = QtCore.pyqtSignal(str) updateStatus = QtCore.pyqtSignal(int) def __init__(self): super(serialThreadC, self).__init__() self.ser = False self.state = 0 self._mutex = QMutex() self.serialEnabled = False def ConnDisconn(self): self._mutex.lock() self.serialEnabled = not self.serialEnabled self._mutex.unlock() def run(self): while True: if self.state == -3 or self.state == -2: self._mutex.lock() if self.serialEnabled: self.updatePB(20) self._mutex.unlock() elif self.state == 0: self._mutex.lock() if self.serialEnabled: self.updatePB(20) self._mutex.unlock()
(NB: if you’re using any kind of IDE or debugger, and you are getting unexpected errors or crashes, your first step in diagnosing the problem should always be to test the code in a standard console. Quite often, the IDE or debugger itself can be the cause of the problem, or may mask error messages comming either from Python or from underlying libraries, such as Qt).