Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using operator 'new' in QObject constructor (multithreading)

This article detailing the "proper" use of QThread says

By the way, one extremely important thing to note here is that you should NEVER allocate heap objects (using new) in the constructor of the QObject class as this allocation is then performed on the main thread and not on the new QThread instance, meaning that the newly created object is then owned by the main thread and not the QThread instance. This will make your code fail to work. Instead, allocate such resources in the main function slot such as process() in this case as when that is called the object will be on the new thread instance and thus it will own the resource.

I understand the first part that the allocation of heap objects (and thus running a constructor) happens in the thread that created the QObject. Only after that it is pushed to the QThread using QThread::moveToThread.

However I don't quite understand the ownership issue mentioned, specifically

meaning that the newly created object is then owned by the main thread

It certainly is not referring standard ownership of resources because in Qt that can be easily controlled by parent-children mechanism.

Does it mean underlying memory management mechanisms of multithreading (or QThread although that is just a wrapper of OS' thread implementation as far as I know)? As in that there is a different "memory segment/cache" for each thread where its resources are stored?

If that is the case I could see the problem but I cannot find the full answer or explanation. Thanks!

like image 658
Resurrection Avatar asked Mar 21 '23 14:03

Resurrection


1 Answers

It's referring to the fact that QObjects live in certain threads -- by default, the thread that created them. "Live" refers to the fact that QObject isn't reentrant nor thread safe: a QObject can be used only from the thread it lives in.

The advice is wrong, though. It's perfectly safe to do

class Foo : public QObject {
    Q_OBJECT
    Foo() {
        m_suboject = new Bar(this);
        m_other = new Fie("/dev/blah", this);
        ...

The key is in that this parameter, meaning the subobjects are correctly parented to the Foo instance. Hence, when you do

Foo *foo = new Foo;
foo->moveToThread(thread);

the whole object tree rooted in "foo" will be moved into the new thread (moveToThread moves an object and all its children).

like image 200
peppe Avatar answered Apr 27 '23 20:04

peppe