[PyQt] Multithreading, signals, reference counting and crash

Ilya Kulakov kulakov.ilya at gmail.com
Thu Feb 11 18:04:38 GMT 2016


Hi Phil,

Wouldn't it make sense to keep Python objects alive while they are queued to be delivered? I don't know the details of implementation, but refcount could be incremented before object enters queue and decremented after all handlers are called.

C++ didn't have luxury of reference counting when signaling was designed in Qt, I don't think it can be used as reference example.

Best Regards
Ilya Kulakov

> On 11 февр. 2016 г., at 19:32, Phil Thompson <phil at riverbankcomputing.com> wrote:
> 
>> On 11 Feb 2016, at 1:14 pm, Ilya Volkov <nxsofsys at gmail.com> wrote:
>> 
>> Hi all,
>> 
>> We have some problems with sending python objects between threads by signals with Qt.QueuedConnection type. If python object was sent from one thread to another by signal, its reference counter does not increment, and object can be destroyed in first thread before slot in second thread will be called which leads to a crash.
>> 
>> I made example which illustrates this situation (also available on github: https://gist.github.com/nxsofsys/d80487e7605eb7061d88 ):
>> 
>> from PyQt5.QtCore import QObject, QThread, pyqtSignal
>> from PyQt5.QtCore import qDebug, QCoreApplication, Qt
>> import time
>> import sys
>> 
>> class SomeThread(QObject):
>> 
>>    signal = pyqtSignal(QObject)
>> 
>>    def __init__(self):
>>        QObject.__init__(self)
>>        self.thread = QThread()
>>        self.wait = self.thread.wait
>>        #
>>        # self.signal.connect(self.signalHandler, type = Qt.QueuedConnection)
>>        self.moveToThread(self.thread)
>>        self.signal.connect(self.signalHandler, type = Qt.QueuedConnection)
>>        self.thread.started.connect(self.run)
>>        self.thread.start()
>> 
>>    def signalHandler(self, obj):
>>        qDebug("signalHandler obj ref counts: " + str(sys.getrefcount(obj)))
>> 
>>    def run(self):
>>        qDebug("Thread sleeps for 1 sec")
>>        time.sleep(1)
>>        qDebug("Process events now")
>>        QCoreApplication.processEvents()
>>        self.thread.exit()
>> 
>> app = QCoreApplication([])
>> th = SomeThread()
>> 
>> # obj = QObject()
>> def someFunc():
>>    obj = QObject()
>>    qDebug("obj ref counts before emit: " + str(sys.getrefcount(obj)))
>>    th.signal.emit(obj)
>>    qDebug("obj ref counts after emit: " + str(sys.getrefcount(obj)))
>> 
>> someFunc()
>> qDebug("Process main events")
>> QCoreApplication.processEvents()
>> qDebug("Process main events done")
>> th.thread.wait()
>> 
>> I think in this case signal emit should increment reference counter for a python object preventing it from destruction. Just comment "obj = QObject" in someFunc and uncomment  "# obj = QObject()" before function definition, and script runs without crash.
> 
> When would you then decrement the reference count again?
> 
> It's up to you to make sure that objects you emit stay alive - a C++ version of your example would crash as well.
> 
> Phil
> _______________________________________________
> PyQt mailing list    PyQt at riverbankcomputing.com
> https://www.riverbankcomputing.com/mailman/listinfo/pyqt


More information about the PyQt mailing list