简体   繁体   English

是否可以从渲染线程强制执行主线程(GUI 线程)?

[英]Is it possible to force execute main thread (GUI thread) from render thread?

We have commands queue which executed in render thread, directly between QQuickWindow::beforeRendering and QQuickWindow::afterRendering , commands do changes on data model, and when data model changes it immediately notify our custom OpenGL render engine to sync data.我们有在渲染线程中执行的命令队列,直接在QQuickWindow::beforeRenderingQQuickWindow::afterRendering ,命令对数据模型进行更改,当数据模型更改时,它立即通知我们的自定义 OpenGL 渲染引擎同步数据。

The problem is when data model changes it also notify subscribers who aim to update UI.问题是当数据模型更改时,它也会通知旨在更新 UI 的订阅者。 But it is error prone approach to update UI from different thread.但是从不同的线程更新 UI 是一种容易出错的方法。 One way is using Qt::QueuedConnection .一种方法是使用Qt::QueuedConnection This is error prone too because when it come executed model may go to far state.这也很容易出错,因为执行时模型可能会进入远态。

Design is very similar to this example .设计与这个例子非常相似。

Is it possible for example update QStadardItemModel linked with QML from render thread?例如,是否可以从渲染线程更新与 QML 链接的QStadardItemModel

It is possible, you can do it with the QMetaObject::invokeMethod(QObject *context, Functor function, Qt::ConnectionType type = Qt::AutoConnection, FunctorReturnType *ret = nullptr) overload of QMetaObject::invokeMethod .有可能,您可以使用QMetaObject::invokeMethod(QObject *context, Functor function, Qt::ConnectionType type = Qt::AutoConnection, FunctorReturnType *ret = nullptr)QMetaObject::invokeMethod(QObject *context, Functor function, Qt::ConnectionType type = Qt::AutoConnection, FunctorReturnType *ret = nullptr)重载来QMetaObject::invokeMethod

For the context you need to pass an object living on thread you want to run your function.对于上下文,您需要传递一个生活在要运行函数的线程上的对象。 For the GUI thread, QCoreApplication::instance() is a good candidate.对于 GUI 线程, QCoreApplication::instance()是一个不错的选择。 For the connection type, use Qt::QueueConnection or Qt::BlockingQueuedConnection depending on your needs (weither you need the call to block or not).对于连接类型,根据您的需要使用Qt::QueueConnectionQt::BlockingQueuedConnection (您是否需要调用阻塞)。 If you are using Qt::BlockingQueuedConnection , make sure that you are not currently in the main thread (you could do a check and pass Qt::DirectConnection if that is the case).如果您正在使用Qt::BlockingQueuedConnection ,请确保您当前不在主线程中(如果是这种情况,您可以进行检查并通过Qt::DirectConnection )。

And for the functor, a lambda will do the trick.而对于函子来说,一个 lambda 就可以解决问题。

In example :例如:

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

This outputs:这输出:

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

You could get inspiration from Calling Qt Functions From Unix Signal Handlers您可以从Calling Qt Functions From Unix Signal Handlers 中获得灵感

You might have some central data structure, eg some std::deque containing lambda expressions ;您可能有一些中央数据结构,例如一些包含lambda 表达式的std::deque let's call it your todo list.让我们称之为您的待办事项清单。 That todo list also manages a pipe(7) (so two file descriptors).该待办事项列表还管理一个管道(7) (所以两个文件描述符)。

You would protect that todo list with appropriate std::mutex and use std::condition_variable您将使用适当的std::mutex保护该待办事项列表并使用std::condition_variable

You would synchronize between the render thread and the main Qt thread by using some pipe(7) or fifo(7) , then use (in the main thread) some QSocketNotifier for synchronization.您将通过使用一些pipe(7)fifo(7)在渲染线程和主 Qt 线程之间进行同步,然后使用(在主线程中)一些QSocketNotifier进行同步。 The render thread would also write(2) some byte to that fifo or pipe when adding a closure to your todolist, and the main thread would use QSocketNotifier to read(2) it, then fetch (pop) a closure from your todo list and run it.在向您的 todolist 添加闭包时,渲染线程还会向该 fifo 或管道写入(2)一些字节,并且主线程将使用QSocketNotifier读取(2)它,然后从您的 todo 列表中获取(弹出)一个闭包并运行。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM