【问题标题】:Is it possible to force execute main thread (GUI thread) from render thread?是否可以从渲染线程强制执行主线程(GUI线程)?
【发布时间】:2021-01-05 17:34:54
【问题描述】:

我们有在渲染线程中执行的命令队列,直接在QQuickWindow::beforeRenderingQQuickWindow::afterRendering 之间,命令对数据模型进行更改,当数据模型发生更改时,它会立即通知我们的自定义 OpenGL 渲染引擎同步数据。

问题在于,当数据模型发生变化时,它也会通知打算更新 UI 的订阅者。 但是从不同的线程更新 UI 是容易出错的方法。一种方法是使用Qt::QueuedConnection。这也容易出错,因为执行时模型可能会进入远状态。

设计与example非常相似。

是否可以从渲染线程更新与 QML 链接的 QStadardItemModel

【问题讨论】:

  • 为什么在渲染 UI 时执行命令?只是不要那样做。渲染应该只使用当前数据/状态绘制 UI,而不是更改任何数据/状态。
  • 因为命令可能会导致渲染器状态的变化。例如,我们有命令 AddBoxCommand。执行此命令时,它会通知渲染器添加的新对象,渲染器通过更改其状态来对此事件做出反应。渲染器直接在beforeRenderingafterRendering 之间工作,所以我们不能在他工作时从不同的线程改变他的状态。 Windows 上的 Qt Quick 使用线程渲染循环,因此渲染线程永远不会锁定。主(Gui)线程通过锁定自己来启动与渲染线程的同步。主线程保持锁定,而渲染线程仍在工作。
  • 我们希望渲染器在 Qt Quick 的渲染线程中完成他的工作,因为 OpenGL 上下文共享可能存在问题。这就是我们在beforeRenderingafterRendering 之间注入命令执行的原因。

标签: c++ multithreading qt opengl qml


【解决方案1】:

有可能,你可以通过QMetaObject::invokeMethodQMetaObject::invokeMethod(QObject *context, Functor function, Qt::ConnectionType type = Qt::AutoConnection, FunctorReturnType *ret = nullptr) 重载来做到这一点。

对于上下文,您需要传递一个生活在要运行函数的线程上的对象。对于 GUI 线程,QCoreApplication::instance() 是一个不错的选择。 对于连接类型,根据您的需要使用Qt::QueueConnectionQt::BlockingQueuedConnection(无论您是否需要调用阻止)。如果您使用Qt::BlockingQueuedConnection,请确保您当前不在主线程中(如果是这种情况,您可以检查并通过Qt::DirectConnection)。

对于函子,lambda 可以解决问题。

例如:

qDebug() << "1 main thread" << QThread::currentThreadId();
QtConcurrent::run([] {
    qDebug() << "1 QtConcurrent thread" << QThread::currentThreadId();

    QMetaObject::invokeMethod(QCoreApplication::instance(), [] {
        qDebug() << "invokeMethod thread" << QThread::currentThreadId();
    }, Qt::BlockingQueuedConnection);

    qDebug() << "2 QtConcurrent thread" << QThread::currentThreadId();
});
qDebug() << "2 main thread" << QThread::currentThreadId();

这个输出:

1 main thread 0x1c7c
2 main thread 0x1c7c
1 QtConcurrent thread 0x19ec
invokeMethod thread 0x1c7c
2 QtConcurrent thread 0x19ec

【讨论】:

  • 我需要更改与主(gui)线程相关的 QStandardItemModel 并通知 QML 视图有关更改。据我所知,从不同的线程更改模型并不是一个好主意(stackoverflow.com/a/46661870/12847278)。在我们的应用程序中,当命令执行时,它可以更改域模型。如果确实如此,则发出事件。此事件有两个订阅者。一个 QStandardItemModel 必须反映自己状态的这种变化。其次是我们的自定义渲染引擎,它使用 OpenGL 上下文,因此在 Qt Quick 渲染线程中工作。
  • 如果有一种方法可以在主(Gui)线程中执行代码,即使它被锁定以与渲染线程同步但似乎不可能。
【解决方案2】:

你可以从Calling Qt Functions From Unix Signal Handlers获得灵感

您可能有一些中心数据结构,例如一些std::deque 包含lambda expressions;让我们称它为您的待办事项列表。该待办事项列表还管理一个pipe(7)(即两个文件描述符)。

您将使用适当的std::mutex 保护该待办事项列表并使用std::condition_variable

您可以使用一些pipe(7)fifo(7) 在渲染线程和主Qt 线程之间进行同步,然后使用(在主线程中)一些 QSocketNotifier 用于同步。渲染线程也将write(2) 一些字节添加到您的待办事项列表时,该fifo 或管道,主线程将使用QSocketNotifierread(2) 它,然后从您的待办事项列表中获取(弹出)一个闭包并运行它。

【讨论】:

    猜你喜欢
    • 2017-06-19
    • 1970-01-01
    • 2010-11-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多