c++ - QThread for an indefinite non-looping lifecycle - Stack Overflow

I understand how QThread is supposed to be used conceptually, with something akin to the followingMyWo

I understand how QThread is supposed to be used conceptually, with something akin to the following

MyWorker *worker = new MyWorker();
QThread *thread = new QThread();
worker->moveToThread(thread);
connect(thread, SIGNAL(started()), worker, SLOT(doWork()));
connect(worker, SIGNAL(done()), thread, SLOT(quit()));
connect(worker, SIGNAL(done()), worker, SLOT(deleteLater()));
connect(thread, SIGNAL(finished()), thread, SLOT(deleteLater()));
thread->start();

which will execute the doWork() method within MyWorker within the thread, stopping the thread and performing memory cleanup once worker announces it's done via done(). This is how I've seen all examples, and it really appears geared towards a Worker does an operation in a separate thread type of use case.

Based on what I've understood:

  • Qt::AutoConnection will use Qt::DirectConnection if the signal and slot objects live in the same thread
  • Qt::AutoConnection will use Qt::QueuedConnection if the signal and slot objects live in a different thread
  • Qt::DirectConnection will execute the destination slot in the same thread as the source signal
  • Qt::QueuedConnection will queue the signal such that it will (eventually) be executed within the event loop of destination slot's thread
  • moveToThread() "relocates" an object from its current thread into the specified one
  • A QThread will remain active (regardless of the objects within) until QThread::quit() is called (which is why the connect to SLOT(quit()) in the above code example is so important

So this leads me to two questions for a potential use case which feels like it goes a little against the prescribe QThread formula, and which I haven't really been able to find discussed anywhere.

Question 1 - Would simply moving the object just allow for slots to execute in the new thread?

By this I mean:

MyWorker *worker = new MyWorker();
QThread *workerThread = new QThread();
worker->moveToThread(workerThread);
connect(workerThread, SIGNAL(finished()), worker, SLOT(deleteLater()));
connect(workerThread, SIGNAL(finished(), thread, SLOT(deleteLater()));
workerThread->start();

In the above, there is no dedicated "execute" slot/method within MyWorker, meaning that it would perform any triggers slots in thread (provided the connection was done using Qt::AutoConnection or Qt::QueuedConnection of course). Thus if elsewhere in the code I were to do:

MyTrigger trigger;
connect(&trigger, SIGNAL(triggered()), worker, SLOT(doSomething()));

then any time trigger would emit the triggered() signal, worker would execute the doSomething() slot within workerThread rather than whatever thread is trigger belongs to? Thus, slot processing would be multi-threaded without there being an explicit doWork() item to be executed in the thread.

Note: I can see the issues with memory management in the example above, but this is just purely an illustration of the idea.

I'm fairly confident that this would be the case, and if so this leads me to my next question...

Question 2 - What thread is an object created in?

Or perhaps a slightly different way to phrase this would be, can I ensure that objects are created in a particular thread? Continuing from the above example, let's say that I want to replace MyWorker with some kind of wider capability, where I want the whole capability to operate in this separate thread. I'm thinking of essentially expanding on the above idea, but rather than using a single class, leveraging a series of classes, all of which are built-out from a central class. What I'm thinking is something along the lines of

class MyCapability: public QObject {
    Q_OBJECT

public:
    MyOtherWorker* getOtherWorker() {
        return worker2;
    }

public slots:
    void setup() {
        worker1 = new MyWorker();
        worker2 = new MyOtherWorker();

        connect(worker1, SIGNAL(something()), worker2, SLOT(other()));
    }

    void doSomething() {
        worker1->doSomething();
    }

private:
    MyWorker *worker1;
    MyOtherWorker *worker2;
};

MyCapability *capability = new MyCapability();
QThread *capabilityThread = new QThread();
capability->moveToThread(capabilityThread);
connect(capabilityThread, SIGNAL(started()), capability, SLOT(setup()));
connect(capabilityThread, SIGNAL(finished()), capability, SLOT(deleteLater()));
connect(capabilityThread, SIGNAL(finished(), thread, SLOT(deleteLater()));
capabilityThread->start();

Thus:

  • capability will live in the capabilityThread
  • since MyCapability::setup() is called within capabilityThread anything created within will belong to capabilityThread as well
  • worker1::something() connects to worker2::other() as Qt::DirectConnection as they are within the same thread

When this is used from external

MyTrigger trigger;
connect(&trigger, SIGNAL(doSomethingTrigger()), capability, SLOT(doSomething()));
connect(&trigger, SIGNAL(doOtherTrigger()), capability->getOtherWorker(), SLOT(other()));

trigger will be connecting via Qt::QueueConnection as capability, worker1, and worker2 all live within the capabilityThread (assuming trigger is in a different one).

What I'm thinking is creating a "pocket" of functionality in this manner that exists in a single thread, separate from the rest of the application, such that the rest of the application can largely ignore what thread it may (or may not) live in, but which will perform its processing/execution in its dedicated thread via signals/slots in a safe and reliable manner.

Note: again, this example code is a bit of a minefield in terms of memory stability/management, but I'm trying to make sure that my understanding of the QThread concepts is correct.

Is my understanding on these two fronts correct? What mistaken assumptions am I working from?

Thanks!

I understand how QThread is supposed to be used conceptually, with something akin to the following

MyWorker *worker = new MyWorker();
QThread *thread = new QThread();
worker->moveToThread(thread);
connect(thread, SIGNAL(started()), worker, SLOT(doWork()));
connect(worker, SIGNAL(done()), thread, SLOT(quit()));
connect(worker, SIGNAL(done()), worker, SLOT(deleteLater()));
connect(thread, SIGNAL(finished()), thread, SLOT(deleteLater()));
thread->start();

which will execute the doWork() method within MyWorker within the thread, stopping the thread and performing memory cleanup once worker announces it's done via done(). This is how I've seen all examples, and it really appears geared towards a Worker does an operation in a separate thread type of use case.

Based on what I've understood:

  • Qt::AutoConnection will use Qt::DirectConnection if the signal and slot objects live in the same thread
  • Qt::AutoConnection will use Qt::QueuedConnection if the signal and slot objects live in a different thread
  • Qt::DirectConnection will execute the destination slot in the same thread as the source signal
  • Qt::QueuedConnection will queue the signal such that it will (eventually) be executed within the event loop of destination slot's thread
  • moveToThread() "relocates" an object from its current thread into the specified one
  • A QThread will remain active (regardless of the objects within) until QThread::quit() is called (which is why the connect to SLOT(quit()) in the above code example is so important

So this leads me to two questions for a potential use case which feels like it goes a little against the prescribe QThread formula, and which I haven't really been able to find discussed anywhere.

Question 1 - Would simply moving the object just allow for slots to execute in the new thread?

By this I mean:

MyWorker *worker = new MyWorker();
QThread *workerThread = new QThread();
worker->moveToThread(workerThread);
connect(workerThread, SIGNAL(finished()), worker, SLOT(deleteLater()));
connect(workerThread, SIGNAL(finished(), thread, SLOT(deleteLater()));
workerThread->start();

In the above, there is no dedicated "execute" slot/method within MyWorker, meaning that it would perform any triggers slots in thread (provided the connection was done using Qt::AutoConnection or Qt::QueuedConnection of course). Thus if elsewhere in the code I were to do:

MyTrigger trigger;
connect(&trigger, SIGNAL(triggered()), worker, SLOT(doSomething()));

then any time trigger would emit the triggered() signal, worker would execute the doSomething() slot within workerThread rather than whatever thread is trigger belongs to? Thus, slot processing would be multi-threaded without there being an explicit doWork() item to be executed in the thread.

Note: I can see the issues with memory management in the example above, but this is just purely an illustration of the idea.

I'm fairly confident that this would be the case, and if so this leads me to my next question...

Question 2 - What thread is an object created in?

Or perhaps a slightly different way to phrase this would be, can I ensure that objects are created in a particular thread? Continuing from the above example, let's say that I want to replace MyWorker with some kind of wider capability, where I want the whole capability to operate in this separate thread. I'm thinking of essentially expanding on the above idea, but rather than using a single class, leveraging a series of classes, all of which are built-out from a central class. What I'm thinking is something along the lines of

class MyCapability: public QObject {
    Q_OBJECT

public:
    MyOtherWorker* getOtherWorker() {
        return worker2;
    }

public slots:
    void setup() {
        worker1 = new MyWorker();
        worker2 = new MyOtherWorker();

        connect(worker1, SIGNAL(something()), worker2, SLOT(other()));
    }

    void doSomething() {
        worker1->doSomething();
    }

private:
    MyWorker *worker1;
    MyOtherWorker *worker2;
};

MyCapability *capability = new MyCapability();
QThread *capabilityThread = new QThread();
capability->moveToThread(capabilityThread);
connect(capabilityThread, SIGNAL(started()), capability, SLOT(setup()));
connect(capabilityThread, SIGNAL(finished()), capability, SLOT(deleteLater()));
connect(capabilityThread, SIGNAL(finished(), thread, SLOT(deleteLater()));
capabilityThread->start();

Thus:

  • capability will live in the capabilityThread
  • since MyCapability::setup() is called within capabilityThread anything created within will belong to capabilityThread as well
  • worker1::something() connects to worker2::other() as Qt::DirectConnection as they are within the same thread

When this is used from external

MyTrigger trigger;
connect(&trigger, SIGNAL(doSomethingTrigger()), capability, SLOT(doSomething()));
connect(&trigger, SIGNAL(doOtherTrigger()), capability->getOtherWorker(), SLOT(other()));

trigger will be connecting via Qt::QueueConnection as capability, worker1, and worker2 all live within the capabilityThread (assuming trigger is in a different one).

What I'm thinking is creating a "pocket" of functionality in this manner that exists in a single thread, separate from the rest of the application, such that the rest of the application can largely ignore what thread it may (or may not) live in, but which will perform its processing/execution in its dedicated thread via signals/slots in a safe and reliable manner.

Note: again, this example code is a bit of a minefield in terms of memory stability/management, but I'm trying to make sure that my understanding of the QThread concepts is correct.

Is my understanding on these two fronts correct? What mistaken assumptions am I working from?

Thanks!

Share Improve this question edited Mar 11 at 14:21 Some programmer dude 410k36 gold badges414 silver badges644 bronze badges asked Mar 11 at 14:15 cancechcancech 1077 bronze badges 3
  • 5 Please don't use the SIGNAL and SLOT macros. They are left over from Qt4 and can hide important problems until runtime. – Botje Commented Mar 11 at 14:24
  • At question 1, how would you trigger the doWork() method? When you move that object to another thread, the event processing of that object will move to the new thread - but if there will be no events, nothing would be executed. – zerocukor287 Commented Mar 11 at 15:20
  • In question 1 the goal is to not have the doWork() method at all. Basically just place the object in the thread and not have a dedicated piece of work to do, waiting for any slots to be triggered and leaving those to execute in the thread (provided they are connected properly of course) – cancech Commented Mar 11 at 15:24
Add a comment  | 

1 Answer 1

Reset to default 4

Thus, slot processing would be multi-threaded without there being an explicit doWork() item to be executed in the thread

Yes, whole point of workers is to not have explicit while (haveWork()) doWork() (it's kinda still there though), just an eventloop that ready to execute tasks as soon as they arrive.

What thread is an object created in?

Function creates an object in a thread it runs in (current thread). setup() will be executed in capabilityThread, so worker1 and worker2 will be created and live in capabilityThread.

Notice that if you want to access data from another thread not by signal-slot mechanism, you need to use syncronisation (QMutex) (like in capability->getOtherWorker()).

You can validate all your assumptions about what runs where using qDebug() << QThread::currentThreadId();

such that the rest of the application can largely ignore what thread it may (or may not) live in

It will only work if you do every data access using signal-slot mechanism (request data, send data). Otherwise you need syncronisation.

Take a look at Qt Concurrent. It has some nice high level multithreading primitives (QFuture, QPromise, map-reduce, and thread pool).

发布者:admin,转转请注明出处:http://www.yc00.com/questions/1744789183a4593809.html

相关推荐

  • c++ - QThread for an indefinite non-looping lifecycle - Stack Overflow

    I understand how QThread is supposed to be used conceptually, with something akin to the followingMyWo

    10小时前
    30

发表回复

评论列表(0条)

  • 暂无评论

联系我们

400-800-8888

在线咨询: QQ交谈

邮件:admin@example.com

工作时间:周一至周五,9:30-18:30,节假日休息

关注微信