Search code examples
c++multithreadingqtqwidgetqthread

Difference between Qt::QueuedConnection and Qt::DirectConnection when connecting a signal to a lambda


Let's consider a scenario of two QObjects, senderObject of type SenderObject and receiverObject of type ReceiverObject. senderObject has been created on a worker thread while receiverObject has been created on the main thread. Now let's asssume SenderObject has a signal somethingsChanged and let's consider the following the code snippet:

Snippet1:

int main(int argc, char *argv[]) {
  QCoreApplication a(argc, argv);
  QThread t;
  SenderObject senderObject = new SenderObject();
  senderObject.moveToThread(&t);
  ReceiverObject receiverObject = new ReceiverObject();
  //in this case we know for a fact that receiverObject's 
  //onSomethingsChanged() will be called on the main thread
  QObject::connect(senderObject, &SenderObject::somethingsChanged, 
  receiverObject, &ReceiverObject::onSomethingsChanged, Qt::QueuedConnection);
  t.start();
  return a.exec();
}

Now let's consider the following code snippet:

Snippet2:

int main(int argc, char *argv[]) {
  QCoreApplication a(argc, argv);
  QThread t;
  SenderObject senderObject = new SenderObject();
  senderObject.moveToThread(&t);
  //where will qDebug() << "Hello, world" be executed? 
  //On the main thread again since the lambda lives on the main thread?
  QObject::connect(senderObject, &SenderObject::somethingsChanged, 
  []{qDebug << "Hello, world";});
  t.start();
  return a.exec();
}

Will the lambda in Snippet2 be execute on the main thread also since the lambda has been declared on the main thread, or will it get executed in the same thread as senderObject?


Solution

  • Here's our class SenderObject:

    class Sender : public QObject {
      Q_OBJECT
     public:
      explicit Sender() : QObject() {
        _timer.setInterval(1000);
        connect(&_timer, &QTimer::timeout, this,
                [this] { emit somethingsChanged(); });
        _timer.start();
      }
    
     signals:
      void somethingsChanged();
    
     private:
      QTimer _timer;
    };
    

    Since there is no overload of QObject::connect that will take a lambda and a Qt connection type parameter, Snippet2 will execute in the sender's thread, which is a worker thread.

    In order to force the lambda to execute on the main thread, here is a workaround:

    int main(int argc, char *argv[]) {
      QCoreApplication a(argc, argv);
      QThread t;
      SenderObject senderObject = new SenderObject();
      senderObject.moveToThread(&t);
      QObject::connect(senderObject, &SenderObject::somethingsChanged, new QObject,
      []{qDebug << "Hello, world";}, Qt::QueuedConnection);
      t.start();
      return a.exec();
    }
    

    Since new QObject is called on the main thread, the lambda will also get executed on the main thread.

    Or, if we don't like to see new, this is what will work:

    int main(int argc, char *argv[]) {
      QCoreApplication a(argc, argv);
      QThread t;
      SenderObject senderObject = new SenderObject();
      senderObject.moveToThread(&t);
      QObject object;
      QObject::connect(senderObject, &SenderObject::somethingsChanged, &object,
      []{qDebug << "Hello, world";}, Qt::QueuedConnection);
      t.start();
      return a.exec();
    }
    

    Or, even a better solution will be to use qApp, which is an instance of the application and a singleton and available from anywhere.

    int main(int argc, char *argv[]) {
      QCoreApplication a(argc, argv);
      QThread t;
      SenderObject senderObject = new SenderObject();
      senderObject.moveToThread(&t);
      QObject::connect(senderObject, &SenderObject::somethingsChanged, qApp,
      []{qDebug << "Hello, world";}, Qt::QueuedConnection);
      t.start();
      return a.exec();
    }