QtSerialPort 在错误的线程中实例化,导致信号/插槽失败

Posted

技术标签:

【中文标题】QtSerialPort 在错误的线程中实例化,导致信号/插槽失败【英文标题】:QtSerialPort instantiating in wrong thread, causing signals/slots to fail 【发布时间】:2013-02-16 04:28:50 【问题描述】:

我正在使用 QtSerialPort 库通过 USB 与虚拟 COM 端口通信。 COM 端口在使用 QtSerialPort 给出的示例项目进行测试时返回数据并正常工作,但在我将其作为项目的一部分运行时失败。

我检查了导致 QtSerialPort 被实例化的实例化链和线程,发现有些奇怪。结果如下。

main()
  MainWindow (Thread 0xbf8dbe0)        // Thread "A"
    HardwareManager (Thread 0xbf8dbe0) // Thread "A"
      QSerialPort (Thread 0xbfb95f0)   // Thread "B" !?

在我的代码中,main() 函数实例化了 MainWindow,而 MainWindow 又实例化了 HardwareManager 并将其存储为私有变量。当 HardwareManager 被实例化时,它也会实例化 QSerialPort 实例,以便它可以正确地与 COM 端口通信。

但是,您会注意到上面我的 QSerialPort 与父对象以及它的父对象位于 不同的线程 中(它在线程 B 中,而两个祖先都在线程 A 中) .我认为这个其他线程导致我的信号/插槽失败。如果我dumpObjectInfo,它会将我的信号/插槽列为已设置,但事件永远不会触发。

this->serial = new QSerialPort();
connect(this->serial, SIGNAL(readyRead()), this, SLOT(readSerialData());

上面是我用来创建新串行端口并将其连接到正确插槽的代码。实际的波特率、奇偶校验和数据/停止位配置分别发生(并且工作正常,在 QtSerialPort 提供的示例应用程序中进行了测试)。

有没有人知道为什么这个特定对象(QSerialPort 实例)在不同的线程中被实例化?我试过“moveToThread”来切换线程关联,但似乎没有任何效果。

我也发了post on the Qt Project Forums,但还没有有用的回复。

编辑: 以下是调用链中的相关代码:

// main()
QApplication a(argc, argv)
MainWindow window = new MainWindow(); // [1]
MainWindow.show();
return a.exec();

// MainWindow::MainWindow() [1]
this->toolController = new QtToolController(this);
HardwareManager *manager = new HardwareManager(this->toolController); // [2]

// HardwareManager::HardwareManager() [2]
this->serial = new QSerialPort();
connect(this->serial, SIGNAL(readyRead()), this, SLOT(readSerialData()));

当一个 QSerialPort 准备好被读取时(它有数据要提供),它会触发readyRead 信号(至少,它应该如此)。此信号在 Qt 示例项目中正确触发,但我从未在我的应用程序中收到该信号。我相信我没有收到信号的原因是因为这些线程问题。

【问题讨论】:

我不知道 QT 是如何工作的,所以只是一个疯狂的猜测:你是在创建 QSerialPort 以响应某些事件吗?该事件处理程序是否有可能在不同线程的上下文中调用? 不。 QSerialPort 实例化在 HardwareManager 实例化中,它在 MainWindow 实例化中,在 main() 方法中调用。此时,没有使用信号/插槽/回调。 我相信你不应该为其他线程而烦恼,你应该只为不工作的信号/插槽而烦恼。请向我们展示您的主要产品。并提供一些关于何时触发插槽的信息 在打扰另一个线程,它只是这样做。我将粘贴更多代码以显示调用链。 似乎您的应用正在加载 Qt 库的两个实例。 QtSerialPort 是否放在共享库中?如果是尝试在发布模式下构建您的应用程序并检查此问题是否仍然存在。我的猜测是您正在调试模式下构建您的应用程序(似乎逻辑,因为您仍在开发和测试您的应用程序)但您在发布模式下链接到 QExtSerialPort。 QExtSerialPort 库正在加载发布 Qt 库,而您的应用正在加载调试 Qt 库。 【参考方案1】:

您可以使用QueuedConnection 来捕获来自不同线程的信号。

connect(this->serial, SIGNAL(readyRead()), 
    this, SLOT(readSerialData()), Qt::QueuedConnection);

这样,一旦控件返回到它的事件循环,槽应该在主线程的上下文中执行。

另外,this post 似乎建议您不要为 QtSerialPort 设置父级(可能是因为 moveToThread 不适用于有父级的 QObjects)。

【讨论】:

添加参数Qt::QueuedConnection应该不是必须的,因为默认的Qt::AutoConnection(我使用的是Qt 5,不确定Qt 4)也能正确处理不同线程之间的连接。跨度> 【参考方案2】:

本着为遇到此问题的其他人提供答案的精神,该问题与发布/调试版本有关。 QtSerialPort 库仅为我的发布环境而构建,无论出于何种原因,在调试模式下运行我的应用程序时都会链接到发布 QtSerialPort,并且线程上下文会丢失。

为了解决这个问题,我确保构建了正确版本的库,然后确保链接到适合我的环境的正确版本。

【讨论】:

以上是关于QtSerialPort 在错误的线程中实例化,导致信号/插槽失败的主要内容,如果未能解决你的问题,请参考以下文章

C++ boost线程在实例化两次时导致分段错误

当前线程不在单线程单元中,因此无法实例化 ActiveX 控件

Arduino 和 QtSerialPort 始终打开错误

如何使用线程实例化多个 QApplication

boost::thread类内成员函数调用返回unique_lock实例化错误

正则化项L1和L2的直观理解及L1不可导处理