简体   繁体   中英

Pyside signals, slots, affinity and object existence

I have this piece of code in a custom widget class:

def import_data(self, fname):
    worker = DataRead(fname)

    readThread = QtCore.QThread(self)
    worker.moveToThread(readThread)

    readThread.started.connect(worker.read_data)
    readThread.start()

The 'worker' class looks like this:

class DataRead(QtCore.QObject):
    def __init__(self):
        super(DataRead, self).__init__()

    @QtCore.Slot()
    def read_data(self):

        print 'Hi!'

The code above does not work. It will only work if I store the worker instance as an attribute of my custom widget class. ie:

def import_data(self, fname):
    self.worker = DataRead(fname)

    readThread = QtCore.QThread(self)
    self.worker.moveToThread(readThread)

    readThread.started.connect(self.worker.read_data)
    readThread.start()        

I can't really get my head round why this is so?

Also, if this is required, why is it not also required that the QThread instance ( readThread ) is also stored as an attribute?

I've googled and seen a lot of talk about 'affinity' and 'persistence' but nothing that really explains it well enough for me.

One final question that comes to mind...if I have to create worker as an instance attribute, does that not negate the moveToThread function I then call? As worker is now linked to my custom widget in the main GUI thread, when self.worker.read_data is called by the thread signal, won't that be executed in the main thread?

My mind is turning to jelly...

The reason you need to store your worker as a class attribute is due to the persistence of variables as you have stated. This has to do with reference counting. Each Python object keeps a tally of how many references there are to itself. For each new reference you make to a variable, the count is incremented. Every time a reference is deleted, the count is decremented. When the reference count reaches zero, the variable is subject to garbage collection and freed from memory.

Your original import_data function defines the worker thread, but as soon as control is returned back to the calling code the reference falls out of scope, worker has its reference count reduced to zero, and is therefore deleted from memory. Since the whole point of threading is to have have parallel execution, your function does not wait when you call readThread.start() and continues executing until it returns. There is no guarantee your worker thread will finish executing before the function returns and the reference is deleted.

The reason that it works when you make worker a member of another class is that the lifetime of the reference is now tied to that class. The reference count does not get decremented when import_data returns. Your worker will persist until either the containing class is deleted, or the reference itself is invalidated.

Creating the worker inside your other class is no problem for moveToThread as you are only storing a reference to the thread. It does not represent where the thread is actually running.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM