c++ - 使用多线程处理 Qt 应用程序中的升压信号

标签 c++ multithreading qt boost-signals2

我有以下问题:我们的主要应用程序使用 Qt 工具包来显示窗口和用户交互。然而,我们应用程序的很大一部分对 GUI 部分一无所知。我现在创建了以下设计:

  • 有一个单例类可能会请求渲染给定对象(OpenSceneGraph 节点;但这与问题无关)
  • 渲染请求导致单例发出信号
  • 在主窗口类(使用 Qt)中有一个插槽来处理渲染对象
  • 目前,该插槽仅创建一个新的文本编辑小部件并将其放置在主窗口的QMdiArea

但是,当我尝试创建新的小部件时,应用程序不可避免地崩溃。错误信息区:

QObject::setParent: Cannot set parent, new parent is in a different thread
[xcb] Unknown request in queue while dequeuing
[xcb] Most likely this is a multi-threaded client and XInitThreads has not been called
[xcb] Aborting, sorry about that.
myApplication: ../../src/xcb_io.c:178: dequeue_pending_request: Assertion `!xcb_xlib_unknown_req_in_deq' failed.
Aborted

在仔细阅读 stackoverflow 后,我发现了类似的问题(这些问题不适用于这种情况)。显然,当我从另一个线程更改主窗口中的某些内容时,Qt 不喜欢它。但是,我没有有意识地创建新线程,我认为单例(在调用 QApplication() 后立即在主函数中创建)应该与 Qt 在同一线程中。显然,我错了。

这是一个显示我正在做的事情的最小示例(我已经提取了代码的相关部分,因此该示例并不完全有效):

class Object
{
public:
};

class Singleton
{
public:
  typedef boost::signals2::signal<void (Object*)> signalShowObject;
  signalShowObject _showObject;
};

class MainWindow : public QMainWindow
{
public:
  MainWindow()
  {
    Singleton::getInstance()->_showObject.connect( boost::bind(&MainWindow::showObject, this, _1) );

    // Set up MDI area etc.
  }

private:
  QMdiArea* _mdiArea;

  void showObject(Object* object)
  {
    // Creating a new subwindow here causes the crash. The `object` pointer is
    // not used and has just been included because it models my real problem
    // better.
    _mdiArea->addSubWindow( new QTextEdit() )->show();
  }
};

我解决这个问题的尝试非常笨拙:

  • 我在 MainWindow 类中创建了一个新的 Qt 信号,其签名与 Boost 信号相同
  • 在处理 Boost 信号的槽中,我发出新的 Qt 信号,将指针传递过来
  • 我现在创建了一个接收指针的新 Qt 插槽

当我在新插槽中打开一个新窗口时,一切正常。然而,这让我觉得非常笨拙。我是否必须像这样级联所有增强信号,还是有更好的方法?

最佳答案

我认为令人困惑的是,渲染请求对单例的调用是从生成请求的任何线程发出的。单例将返回一个唯一的对象,但它发送的信号仍在请求线程的上下文中。必须做一些事情来显式地导致或允许线程上下文切换到主 UI 线程,以便实际处理此信号并在主线程中创建 UI 对象。

并且您正在按照您描述的顺序隐含地执行此操作:

•I created a new Qt signal in the MainWindow class with the same signature as the Boost signal

•In the slot that handles the Boost signal, I emit the new Qt signal, passing the pointer over

•I now created a new Qt slot that receives the pointer

Qt信号和槽自动对跨线程信号进行排队(注1)。所以处理 Boost 信号的槽还在请求线程中。然后它发出 Qt 信号。 Qt 检测到信号的接收者在主线程中(注2)但发送者在请求者线程中,并将信号排队。当主线程中的主 Qt 事件循环从事件列表中拉出这个排队的事件时,它会自动重新发出信号,但现在它在主线程上下文中并且允许 UI 操作。

注意 1 - 除非此行为在 connect() 调用中被显式覆盖 - 请参阅 Qt::ConnectionType 的文档。

注意 2 - 实际上,接收器的 QObject 由主线程拥有。每个 QObject 都保留创建它的线程上下文的线程 ID。

我希望这有助于解释线程发生了什么。您的解决方案很好,但正如@tmpearce 所建议的那样,将内容包装在适配器中可能会很方便。

关于c++ - 使用多线程处理 Qt 应用程序中的升压信号,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/10733702/

相关文章:

c++ - 以高分辨率渲染 QChartView

c++ - 在以读/写/追加模式打开文件后,我可以检测到文件的预先存在吗?

c++ - 在 MKL 中求矩阵的矩阵指数

c++ - 文件未完全解密,AES CBC 模式。第一个 block 没有解密。 WCAPI

c++ - eigen::vectorXf 到 MatriXf 映射

c# - WaitHandle.WaitAny 每次调用时都会分配 WaitHandle[] 的副本

c# - CultureInfo线程安全

java - C++相当于Java import等问题

c++ - 是否曾在大型多线程 C++ 程序中使用过通信顺序进程?

qt - Qt,仅显示大型QImage一部分的低成本方法