默认情况下,从工作线程发出 Qt::signal 会使主线程上的 UI 更新?

Emitting a Qt::signal from worker thread makes the UI update on main thread by default?

我是 Qt 的新手。我有一个 std::thread 的工作线程。工作线程函数在循环中不断获取一些数据。 QML UI 上的 Text 元素经常更新数据大小。我有一个侦听器回调,它只是一个 std::function,它是从 thread's function 调用的。它根据我在 QML 上更新 Text 元素向我发送回调。我使用 signal slot 机制更新它。

以下是 QML : Text 元素:

Text {
  id: mytext
  objectName: "mytextobject"

  function slotUpdateData(someValue){
    mytext = someValue
  }
}

SignalUpdateData 与位于 QML 侧的 slotUpdateData 相连。每次我从 std::thread 获得数据事件回调时,我 emit SignalUpdateData 都会更新 UI 上的 QML Text element

void CallBackReceivedFromWorkerThread(float someValue) {
  emit SignalUpdateData(someValue)
}

以下是我如何将此 C++ signalQML slot

联系起来
QObject::connect(this, SIGNAL(SignalUpdateData(QVariant)), myTextItemQObject, SLOT(slotUpdateData(QVariant)));

所有这一切都很好。没有崩溃、锁定,什么都没有。

根据我的理解,由于worker thread的函数触发了回调,所以当收到回调时,执行控制权在worker thread。所以当执行 emit SignalUpdateData(someValue) 时,我们仍然在工作线程上。 据我之前在 androidjava 中的经验所知,我们无法从应用程序 main thread 之外的任何地方更新 UI。

那么,这是如何运作的? emit SignalUpdateData(someValue) 是否将呼叫放入 main UI thread's event loopQt 是否仍在 main thread 上进行 UI 更改,尽管我从 worker thread 中调用它?如果我的方法没问题,那么它对性能有影响吗?这样做的最佳建议是什么?

我想对此非常确定,而不仅仅是幸运地让它发挥作用。我是否也应该使用 Qt::Connection_enum 以获得最佳方法?

您正在按照预期的方式利用 Qt!而且您不小心 运行 进入了它:这是一个体面设计的标志 - 它 "just works"。你万岁,Qt 万岁:)

之所以有效,是因为 Qt 专门设计用于使其有效,并且您正在使用默认的自动连接,其存在的理由是在这种特定情况下帮助您解决问题。所以你恰好做对了一切:什么都不改变!

当您发出信号时,Qt 获取相关的源和目标对象互斥量,并将接收对象的 thread()QThread::currentThread() 进行比较。如果它们相同,则立即调用 slot/functor:它发生在信号主体中,因此在信号 returns 之前调用槽。这是安全的,因为目标对象是从其 thread() 安全的地方使用的。

如果target->thread() != QThread::currentThread(),则QMetaCallEvent排队到目标对象。该事件包含(等效的)插槽方法指针和插槽传递的任何参数的副本。 QObject::event 实现处理事件并执行调用。目标对象线程的事件循环在调用堆栈上,因为它的工作是将排队的事件传递给对象。

以上就是一个Qt::AutoConnection的概括意思。如果您使用 Qt::QueuedConnection,则无论线程是什么,第二种情况都适用。如果您使用 Qt::DirectConnection,第一种情况无论如何都适用。

我的猜测是,在 SO 上的 Qt 相关问题中,>95% 的非自动连接类型的使用是不必要的,并且源于缺乏理解和求助于魔法咒语。