将信号连接到 lambda 时 Qt::QueuedConnection 和 Qt::DirectConnection 之间的区别

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

让我们考虑两个 QObjectssenderObject 类型 SenderObjectreceiverObject 类型 ReceiverObject 的场景。 senderObject 已在工作线程上创建,而 receiverObject 已在主线程上创建。现在让我们假设 SenderObject 有一个信号 somethingsChanged 并且让我们考虑以下代码片段:

片段 1:

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();
}

现在让我们考虑以下代码片段:

片段 2:

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();
}

Snippet2 中的 lambda 是否也会在主线程上执行,因为 lambda 已在主线程上声明,还是会在与 senderObject 相同的线程中执行?

这是我们的 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;
};

由于没有 QObject::connect 的重载需要一个 lambda 和一个 Qt 连接类型参数,Snippet2 将在发送者的线程中执行,它是一个工作线程。

为了强制 lambda 在主线程上执行,这里有一个解决方法:

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();
}

由于new QObject是在主线程上调用的,lambda也会在主线程上执行。

或者,如果我们不喜欢看到 new,这将起作用:

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();
}

或者,更好的解决方案是使用 qApp,它是应用程序的实例和单例,可从任何地方使用。

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();
}