20

I have following code:

class A : public QObject
{
    Q_OBJECT
public:
    A() : QObject()
    {
         moveToThread(&t);
         t.start();
    }
    ~A()
    {
         t.quit();
         t.wait();
    }

    void doSomething()
    { 
         QMetaObject::invokeMethod(this,"doSomethingSlot");
    }
public slots:
    void doSomethingSlot()
    {
         //do something
         emit ready();
    }
signals:
    void ready();
private:
    QThread t;
}

The question why from doSomething it must be call via QMetaObject::invokeMethod. I know that there is something with connection type. Could some one explain what is under the hood?

krzych
  • 2,126
  • 7
  • 31
  • 50
  • 1
    ecatmur gave you perfect answer. You have another problem, having thread as a member of object which is moved to that thread is very bad idea, during destruction (when using deleteLater) you can have strange problems. – Marek R Dec 19 '12 at 09:05
  • For complete explanation read [this document](http://doc.qt.digia.com/4.2/threads.html#per-thread-event-loop) – Amit Tomar Dec 19 '12 at 09:06
  • @MarekR: what problems? I've get this solution from http://stackoverflow.com/questions/13878745/correct-way-of-threading-in-qt – krzych Dec 19 '12 at 09:09
  • 1
    delateLater invokes destructor in proper thread. When you are destroying this you will also will try to destroy thread. So code will keep thread alive and code will try to destroy the thread: it will hang. So this solution just corrupts functionality of delateLater. – Marek R Dec 19 '12 at 09:21
  • Where is `deleteLater` used? – krzych Dec 19 '12 at 09:36

2 Answers2

36

As you haven't specified a Qt::ConnectionType, the method will be invoked as Qt::AutoConnection, which means that it will be invoked synchronously (like a normal function call) if the object's thread affinity is to the current thread, and asynchronously otherwise. "Asynchronously" means that a QEvent is constructed and pushed onto the message queue, and will be processed when the event loop reaches it.

The reason to use QMetaObject::invokeMethod if the recipient object might be in another thread is that attempting to call a slot directly on an object in another thread can lead to corruption or worse if it accesses or modifies non-thread-safe data.

ecatmur
  • 152,476
  • 27
  • 293
  • 366
  • You mean that if I use A.doSomething() from another thread it may lead to corruption? Why so? Could you describe the mechanism also? – krzych Dec 19 '12 at 09:06
  • 1
    @krzych for example, if you read from a QString at the same time another thread is modifying it then it may appear to be in an inconsistent state, leading to indirecting an invalid pointer. – ecatmur Dec 19 '12 at 09:14
28

I like this trick:

void A:doSomethingSlot()
{
     if (thread()!=QThread::currentThread()) {
         QMetaObject::invokeMethod(this,"doSomethingSlot", Qt::QueuedConnection);
         return;
     }
     // this is done always in same thread
     ...
     emit ready();
}
Marek R
  • 32,568
  • 6
  • 55
  • 140
  • 2
    Don't do that, use compile time check: `QMetaObject::invokeMethod(this, [this]{ doSomethingSlot(); }, Qt::QueuedConnection);` – jaques-sam Mar 18 '22 at 16:39