C++ QT应用程序冻结

C++ QT应用程序冻结,c++,qt,opencv,serial-port,qtgui,C++,Qt,Opencv,Serial Port,Qtgui,我们开发了一个集摄像机和微控制器于一体的系统。 GUI显示来自摄像头的图像和来自微控制器的串行计数,我们使用串行线程轮询来自微控制器的数据,并将信号发送到GUI以显示它,我们还使用单独的线程捕获图像并将其传递给主线程。 应用程序的问题是,当系统处于空闲状态时,GUI冻结,我们必须重新启动应用程序才能开始工作(空闲意味着,用户没有租用任何按钮,计数和图像不断出现)。 这里需要注意的最重要的一点是GUI冻结问题在这里并不一致。安装了多个系统,在某些地方,冻结(无响应)问题每2/3周出现一次,在某些地

我们开发了一个集摄像机和微控制器于一体的系统。 GUI显示来自摄像头的图像和来自微控制器的串行计数,我们使用串行线程轮询来自微控制器的数据,并将信号发送到GUI以显示它,我们还使用单独的线程捕获图像并将其传递给主线程。 应用程序的问题是,当系统处于空闲状态时,GUI冻结,我们必须重新启动应用程序才能开始工作(空闲意味着,用户没有租用任何按钮,计数和图像不断出现)。 这里需要注意的最重要的一点是GUI冻结问题在这里并不一致。安装了多个系统,在某些地方,冻结(无响应)问题每2/3周出现一次,在某些地方,每2天出现一次。等待应用程序响应没有帮助


我的主要问题是GUI冻结的主要原因是什么,是否需要在串行线程和图像捕获线程上执行任何检查以避免不必要的数据传输。

听起来您遇到了并发冲突,在事情运行足够长的时间到最终停止之前,不一定会导致崩溃在正确的时间发生的事件的神奇组合,使事情变得更糟

应用程序中有三个线程:GUI线程、串行线程和摄像头线程。串行线程和摄像头线程从设备收集数据,然后将它们传递到GUI线程进行显示。我假设串行线程和摄像头线程彼此不共享任何数据,因此没有出现问题的风险

如何将数据从串行线程和照相机线程向上传递到GUI线程?这就是你可能遇到问题的地方

大多数复杂的数据结构和Qt类都不是线程安全的,也就是说,它们决不能同时从两个或多个线程读取和写入

以下是一些安全地在线程之间传递数据的策略:

  • 整数在CPU的指令集级别是原子的,因此您可以安全地从多个线程读取和写入整数(或等于或小于整数的任何数据类型,如bool、单个字符或指针),而不会出现任何不一致的状态。必须使用C++的std::atomic模板声明此类变量,以确保编译器不会执行破坏原子性的优化

    任何比整数更大/更复杂的操作都有可能导致一个线程将其一半的数据写入内存,而另一个线程同时读取这一半写入的数据,从而导致非常意外的结果,通常会使应用程序崩溃或使某些内容卡在无限循环中

  • Qt中的信号和插槽是线程安全的。在线程之间传递复杂数据的一种方法是
    发出一个线程中的数据,并让另一个线程中的
    插槽接收该数据。在这种情况下,Qt会为您解决所有并发性问题。这里唯一的问题是,如果数据的消费者线程不能足够快地吸收数据,Qt的事件队列将塞满太多的数据,最终你的应用程序将崩溃,因为新的GUI事件(如鼠标单击、重新绘制事件等)无法再通过阻塞的事件队列

  • 您可以使用
    QMutex
    来确保一次只有一个线程在读取或写入复杂的数据结构
    QMutex
    允许您在一个或多个线程中阻止/暂停执行,同时单个线程“持有”互斥体并允许其执行,对数据执行操作,而不存在其他线程接触该数据的任何风险。当一个线程完成时,它会“释放”互斥体,然后允许另一个线程“持有”互斥体,恢复其执行,以便它可以处理数据


  • 就测试而言,通常情况下,您的应用程序因并发冲突而崩溃的几率随着数据流量的增加而增加。如果您可以人为地提高相机帧数和“序列计数”传递到GUI线程的速率,您将能够更快地再现应用程序崩溃。一旦您成功解决了并发问题,您应该能够向系统注入大量数据,而不会使系统崩溃。

    您是否在捕获线程上编程了重采样器,以便主线程不会充满传入数据?你监控了输入速率吗?@ArneJ我不确定我是否监控了输入速率,你能告诉我如何检查吗?@vallabh对于初学者来说,每次捕获线程接收数据时,都会增加一个整数。在另一个线程中,运行一个休眠一秒钟的循环,然后打印计数器。现在在渲染(主)线程中执行相同的操作。如果您的应用程序是稳定的,那么即使在长时间的会话之后,数字也应该大致相同。如果使用者线程吸收数据的速度不够快,那么可能会出现GUI冻结问题。在这种情况下,上述想法既可以提供证据,也可以提供解决方案。制作人(甚至消费者)可能会检查有多少挂起的图像尚未被消费,并跳过图像以防发生。@vallabh这主意不好,那么你仍然在淹没制作人/捕获线程。要么控制输入速率,要么重新采样(丢弃一些帧)。现在我在线程中使用信号槽机制,正如您所说的,如果消费者线程吸收数据的速度不够快,那么可能会出现GUI冻结问题。什么是我在图像捕获线程中添加一个延迟,并每隔一秒钟发送一次数据一个整数在CPU的指令集级别是原子的,所以你可以安全地读写一个大错误的整数(我曾经有过自己的错误)。您忽略了这一点,例如,整数可能会在CPU寄存器中更新或完全缓存。因此,两个线程可能会对同一个变量使用两个“化身”。此外,编译器可能会重新排序可能会导致错误的赋值