Разница между Qt ::QueuedConnection и Qt ::DirectConnection при подключении сигнала к лямбде

#c #multithreading #qt #qwidget #qthread

#c #многопоточность #qt #qwidget #qthread

Вопрос:

Давайте рассмотрим сценарий из двух QObjects , senderObject типа SenderObject и receiverObject типа ReceiverObject . senderObject был создан в рабочем потоке, в то время receiverObject как был создан в основном потоке. Теперь давайте предположим SenderObject , что сигнал somethingsChanged есть, и рассмотрим следующий фрагмент кода:

Фрагмент1:

 int main(int argc, char *argv[]) {
  QCoreApplication a(argc, argv);
  QThread t;
  SenderObject senderObject = new SenderObject();
  senderObject.moveToThread(amp;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, amp;SenderObject::somethingsChanged, 
  receiverObject, amp;ReceiverObject::onSomethingsChanged, Qt::QueuedConnection);
  t.start();
  return a.exec();
}
  

Теперь давайте рассмотрим следующий фрагмент кода:

Фрагмент2:

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

Будет ли лямбда-код в Snippet2 выполняться в основном потоке также, поскольку лямбда-код был объявлен в основном потоке, или он будет выполняться в том же потоке, senderObject что и?

Ответ №1:

Вот наш класс SenderObject:

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

 signals:
  void somethingsChanged();

 private:
  QTimer _timer;
};
  

Поскольку нет перегрузки QObject::connect , которая будет принимать лямбда-код и Qt параметр типа соединения, Snippet2 будет выполняться в потоке отправителя, который является рабочим потоком.

Чтобы заставить лямбду выполняться в основном потоке, вот обходной путь:

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

Поскольку new QObject вызывается в основном потоке, лямбда-код также будет выполняться в основном потоке.

Или, если нам не нравится видеть new , это то, что будет работать:

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

Или, даже лучшим решением будет использовать qApp , который является экземпляром приложения и синглтона и доступен из любого места.

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