e3oroush
e3oroush

Reputation: 3125

Slots execution policy in multithread applications

I have two thread inherited from QThread. The scenario is object of thread2 emit a signal and object of thread1 have a slot to execute. I expected that slots executes in the thread2 but it executes in the Main thread!!!. Here is my sample code to show that:

Headrs.h

class Thread1 : public QThread{
    Q_OBJECT
public:
    Thread1(){}
protected:
    void run(){
        qDebug() << "run in thread " << this->currentThreadId();
        exec();
    }
private slots:
    void slot1(){
        qDebug() << "slot in " << this->currentThreadId();
    }

signals:
    void sig1();
};
class Thread2 : public QThread{
    Q_OBJECT
protected:
    void run(){
        msleep(100);
        qDebug() << "emit sig2 in: " << this->currentThreadId();
        emit sig2();
    }
signals:
    void sig2();
};

class obj1 : public QObject {
    Q_OBJECT
public:
    obj1(){
        connect(&t2, SIGNAL(sig2()), &t1, SLOT(slot1()));
        connect(this, SIGNAL(sigObj()), &t1, SLOT(slot1()));
        t1.start();
        t2.start();
    }
public:
    void fcn(){
        QThread::msleep(1000);
        emit sigObj();
        qDebug() << "emit sigObj in " << QThread::currentThreadId();
    }
private:
    Thread1 t1;
    Thread2 t2;


signals:
    void sigObj();
};

Main.cpp

QCoreApplication a(argc, argv);
obj1 o1;
o1.fcn();

return a.exec();

What I do expect from this code is to slot1() always execute in thread1 from both emitted signals sig2() and sigObj(). But no matter in what thread we emit the signal, slot1 executes in the main thread. By the way here is my output:

run in thread  0x7ffff6169700  (thread1)
emit sig2 in:  0x7ffff5968700  (thread2)
slot in  0x7ffff7fce740        (main thread)
emit sigObj in  0x7ffff7fce740 (main thread)
slot in  0x7ffff7fce740        (main thread)

Is it something is wrong or always it works like that? And what should I do if I want execute slots in their own threads?

Upvotes: 1

Views: 85

Answers (1)

There's nothing special about QThread. It's just a QObject that happens to also be a handle to a platform-native thread. But the slots of a QThread-derived object work just as if you had a plain QObject. They will execute in the object's thread - and that'll be the thread where you executed the object's constructor, or, in your case, the main thread. This is where things are confusing: the thread() of either of your thread objects is still the main thread, and that's where the slots will run. Just because your QObject is called QThread doesn't make it any different.

The fix is simple: don't override QThread's "run", and don't add functionality to QThread. Instead, explicitly move your objects to the threads where you want them to do their work. It's an antipattern to explicitly call moveToThread on a QThread, so don't do that as a "quick hack".

The only reason in this case to derive from QThread would be to turn it into a proper RAII class by adding quit(); wait(); to its destructor.

Here's how your code might look, fixed:

// https://github.com/KubaO/stackoverflown/tree/master/questions/thread-sigslot-37325348
#include <QtCore>

class Worker1 : public QObject {
   Q_OBJECT
public:
   Q_SIGNAL void sig1();
   Q_SLOT void slot1() {
      qDebug() << "slot in" << thread();
   }
};

class Worker2 : public QObject {
   Q_OBJECT
public:
   Worker2() {
      QTimer::singleShot(100, this, [this]{
         qDebug() << "emit sig2 in" << thread();
         emit sig2();
      });
   }
   Q_SIGNAL void sig2();
};

class Object : public QObject {
   Q_OBJECT
   Worker1 w1;
   Worker2 w2;
   QThread t1, t2;
   Q_SIGNAL void sig();
public:
   Object() {
      t1.setObjectName("t1");
      t2.setObjectName("t2");
      connect(&w2, &Worker2::sig2, &w1, &Worker1::slot1);
      connect(this, &Object::sig, &w1, &Worker1::slot1);
      w1.moveToThread(&t1);
      w2.moveToThread(&t2);
      t1.start();
      t2.start();
      QTimer::singleShot(1000, this, [this]{
         qDebug() << "emit sig in" << thread();
         emit sig();
      });
   }
   ~Object() { t1.quit(); t2.quit(); t1.wait(); t2.wait(); }
};

int main(int argc, char ** argv) {
   QCoreApplication app{argc, argv};
   app.thread()->setObjectName("main_thread");
   Object obj;
   QTimer::singleShot(2000, &app, [&]{ app.quit(); });
   return app.exec();
}

#include "main.moc"

Output:

emit sig2 in QThread(0x7fff4fd98bd0, name = "t2")
slot in QThread(0x7fff4fd98bc0, name = "t1")
emit sig in QThread(0x7fe3dac0aed0, name = "main_thread")
slot in QThread(0x7fff4fd98bc0, name = "t1")

Upvotes: 3

Related Questions