使用QThreads处理QImage

4
以下情景:我有多个视频流,这些流是从OpenCV获取的,并通过实现QQuickImageProvider将其显示在我的QML GUI中。如果图像发生变化,会发出信号,导致GUI中的图像调用提供程序的requestImage(...)函数。我对QImage的底层实现和特别是如果我将requestImage(...)中的其中一个图像传递给GUI所发生的情况并不了解,但是QImage的共享内存原则表明,当我的OpenCV线程更新正在被读取/传递/等待GUI时,可能会出现问题。这正确吗?
我计划采取的方法是向提供程序添加一个QMutex,在图像更新和请求期间都进行锁定,并在请求函数中将所请求的图像复制到新的QImage中,并在其上调用“bits()”函数,这显然应该导致深度复制,然后释放互斥体。这样做有意义吗?而且必要吗?
谢谢
2个回答

1

阅读Qt文档,了解隐式共享如何在线程中工作。基本上,隐式共享是通过原子计数器强制执行的,但仍需要提供线程安全。

我现在计划采取的方法是将 QMutex 添加到提供程序中,在图像更新和请求期间都会锁定它,并在请求功能中将所请求的图像复制到新的 QImage 中并调用其 "bits()" 函数,这显然应该导致深度复制,然后解锁互斥体。这样做有意义吗?这是必要的吗?

文档表示这样做是有意义的,也应该这样做,除了不需要对 QImage 进行深度复制外。基本上,这对于 getter 应该足够了。

QImage getImage() 
{
  m_mutex.lock();
  QImage img(m_image);
  m_mutex.unlock();
  return img;
}

你可以使用QMutexLocker来更好地实现它。

0
QImage是一个具有隐式共享(写时复制)特性的类之一。因此,不需要手动复制QImage并进行锁定;只需调用其拷贝构造函数即可。这个拷贝是浅拷贝的,但当你的线程尝试更新它时,它会自动执行深拷贝。
另外,为什么你不直接在信号中传递QImage呢?

他正在使用“拉取”方法,而传递图像将是“推送”。 - UmNyobe
因此,没有必要手动锁定QImage进行复制。QT文档提供了不同的建议 - 实际上,如果在函数中没有添加线程安全性,可能会导致崩溃。 - bluewater2
@bluewater2:文档明确表示隐式共享和多线程是兼容的。你所接受答案中的锁定实际上是不必要的开销。对于“共享实例的线程安全性不能保证”,这意味着您不能在多个线程中操作指向同一QImage的指针或引用。我不知道您为什么会出现分段错误,但我以前使用过这种方法,并且应用程序验证器根本没有发现任何内存问题。 - Siyuan Ren
@bluewater2:但是如果您采用Qt文档提倡的方法(通过信号传递QImage),则不需要进行锁定。使用信号和槽来处理多线程比手动同步更容易、更安全,而且不容易出现死锁或饥饿等问题。锁定的另一个问题是由锁定保护的区域可能会运行很长时间,阻塞其他所有线程。信号和槽没有这样的问题,因为锁定队列所花费的时间是有限的。 - Siyuan Ren
@C.R. 我本来确实会使用信号的方式,但是我找不到一种方法来在我的 QML 图形用户界面中显示传递的 QImage,而不编写一个新的类来实现这个目的,这似乎比好处带来了太多的编程开销。 - bluewater2
显示剩余3条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接