使用第二个线程快速更新 GUI 窗口会通过调整窗口大小或移动窗口而崩溃

Fast updating GUI window with a second thread crashes by resizing or moving the window

本文关键字:窗口 窗口大小 调整 移动 崩溃 线程 第二个 更新 GUI      更新时间:2023-10-16

我有一个图像采集线程,该线程在使用Visual Studio 2015编译的64位Qt 5.6.0应用程序中每次抓取都会更新GUI。除非用户不调整 GUI 窗口的大小或移动 GUI 窗口,否则应用程序工作正常。但是,如果移动窗口或调整窗口大小,它会随机崩溃。代码摘要如下。

根据调试信息,崩溃是由于QImage::convertToFormat_helper中的访问冲突而发生的 有人知道为什么会发生这种情况以及如何修复吗?

初始化:'

auto thread = new KinectThread();
connect(thread,SIGNAL(OnRgbImage(QImage)),this,SLOT(rgbImageReady(QImage)));
thread->start();

'

工作线程:'

void KinectThread::run()
{
kinect = new Kinect();
while(true)
{
auto img = kinect->getRgb();
QImage imgIn= QImage((uchar*) img.data, img.cols, img.rows, img.step, QImage::Format_RGB888);
emit OnRgbImage(imgIn);
QThread::msleep(20);
}
delete kinect;
}

'

图形用户界面更新:

void MainWindow::rgbImageReady(QImage image)
{
if (!image.isNull())
{
QGraphicsView* view = ui->graphicsView;
auto width = image.width();
auto height = image.height();
m_pScene->setSceneRect(0, 0, width, height);
auto pixmap = QPixmap::fromImage(image);
m_pixmapItem->setPixmap(pixmap);
view->show();
}
}
QImage

构造函数为图像数据获取uchar *data不会创建数据的副本,这意味着它们需要uchar *缓冲区至少与 QImage 实例一样长。

此外,QImage是隐式共享的,这意味着复制QImage也不会复制图像数据,但仍会访问原始缓冲区。

这很好,因为复制图像并通过信号传递速度很快。但这也意味着,来自kinect->getRgb()调用的图像数据必须一直存在,直到信号事件通过事件队列传递到MainWindow::rgbImageReady,直到使用完数据。这也意味着,如果Kinect类更改了已传递给rgbImageReady的现有缓冲区,而您从带有QPixmap::fromImage(image)的图像创建像素图,则会弄得一团糟。如果Kinect类在完成上一个缓冲区之前删除了现有缓冲区(例如,因为它分配了更大的缓冲区,因为窗口大小已更改),则rgbImageReady将出现访问冲突。

通过考虑@E4Z9暗示 QImage 不拥有其数据的所有权,很明显,在第二个线程中销毁 QImage 数据是不成熟的。 因此,将QImage的深度拷贝传递到信号中可以解决问题。

emit OnRgbImage(imgIn.copy());

因此,这是一个不成熟的对象破坏问题,通过不相关的窗口大小调整和移动而暴露出来。