Why does PyQt crashes without information? (exit code 0xC0000409)
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).
brazoayeye
Updated on December 23, 2021Comments
-
brazoayeye over 2 years
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
inrun()
or inConnDisconn()
. 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 allserialThread
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 fromserialThread
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).
-
brazoayeye over 6 yearsI don't get the point. Since you told that the
ConnDisconn(self)
is a different thread thanserialThread
(that makes sense,ConnDisconn
is connected to a GUI signal) the same should be forsw.updateStatus
(that's not in the main thread). In thesw.updateStatus
IMyButton.setText()
, is this operation allowed? And more, is it also forbidden to read variables concurrently using different threads? -
ekhumoro over 6 years@brazoayeye. I did not say that
ConnDisconn
is in a different thread (and it isn't, it lives in the main thread, and is called by the main thread). But I did say thatupdatePB
is okay, because it emits a cross-thread signal, which is guaranteed (by Qt) to be thread-safe. So yes, as a direct consequence of that, it is safe to callsetText()
. This is all standard stuff in PyQt, and there are dozens of similar questions about it on SO. It is never forbidden to read/write anything by multiple threads - that is the whole problem! (And that is what the mutex is for). -
ekhumoro over 6 years@brazoayeye. I should add, though, that for your specific example, the mutex is probably overkill, because (I assume) there is only ever one thread that modifies
serialEnabled
(i.e. the main thread). So your example code looks okay, even though it is not 100% strictly correct. As far as fixing your code is concerned, the most important part of my answer is probably the note at the bottom. The rest just tries to answer the more general questions you asked. -
brazoayeye over 6 yearsif
ConnDisconn
lives in the main thread because it's called by the main thread with signal-slots, why doesn'tsw.updateStatus
lives in theserialThread
since it's called byserialThread
in a mostly identical way? Can you post most relevant questions-tutorial to deepen the topic? -
ekhumoro over 6 years@brazoayeye. Because
serialThread
also lives in the main thread. TheQThread
class is not itself a thread - it is merely a class which manages an underlying thread provided by the OS. So yourserialThread
and its methods all live in the main thread. The only part of your example that is executed outside the main thread is the code in therun
method. This callsupdatePB
, which emits a signal. Qt automatically detects when a signal is sent from one thread to another, and so does it in a thread-safe way (by posting an event to the event-loop of the receiving thread). -
Sourabh Desai over 3 yearsI used anaconda terminal to run the code and voila, I spotted the bug! Thanks @ekhumoro