将QScopedPointer移动到线程

时间:2014-04-08 08:36:36

标签: c++ multithreading qt opencv image-processing

最近几周我读了很多关于RAII的内容,并认为我应该在我的应用程序中开始使用智能指针。作为一个例子,我试图修改我的一个应用程序。它从线程中的网络摄像头捕获帧,在另一个线程中执行图像处理,并在QT小部件中显示已处理和未处理的图像。一个中心对象是CCameraHandler,它控制捕获线程和图像处理线程。到目前为止,我在这个类中使用了4个普通指针作为成员:

CCameraCapture* m_CameraCapture;
CImageProcessor* m_ImageProcessor;
QThread* m_CameraCaptureThread;
QThread* m_ProcessingThread;

在CCameraHandler的构造函数中,我使用new创建了Instances并将捕获对象移动到了线程:

m_CameraCaptureThread= new QThread();
m_CameraCapture= new CCameraCapture();
//Move camera capture object to thread
m_CameraCapture->moveToThread(m_CameraCaptureThread);

这种方法效果很好。现在我想用QScopedPointer进行第一次测试并尝试使用

将m_CameraCapture更改为QScopedPointer
QScopedPointer<CCameraCapture> m_CameraCapture;

并在初始化列表中使用CameraCapture(新的CCameraCapture())初始化它。它很好地编译并且可以正常工作但是当我关闭应用程序时会调用析构函数我从Qt得到一个错误:“无法将事件发送到另一个线程所拥有的对象。当前线程5ff590。接收器”(类型为'CCameraCapture) ')是在线程4b7780中创建的“我猜它与m_CameraCapture-&gt; moveToThread(m_CameraCaptureThread)有关;我现在移动一个范围指针。 QScopedPointer是否自动成为CCameraCapture的父级?到目前为止我用过

//This connections guarantees that the m_CCameraCapture and m_CameraCapture are deleted after calling QThread::exit()
QObject::connect(m_CameraCaptureThread, SIGNAL(finished()), m_CameraCaptureThread, SLOT(deleteLater()));
QObject::connect(m_CameraCaptureThread, SIGNAL(finished()), m_CameraCapture, SLOT(deleteLater()));

在摄像头捕获停止时删除工作线程。如果m_CameraCapture现在是CCameraHandler的父级,可能会导致问题。目前我不太确定在这种情况下使用SmartPointer是否是一个好主意。什么想法可能会导致破坏这个错误?

编辑: CCameraHandler dtor看起来像这样(线程应该在worker之前删除):

CCameraHandler::~CCameraHandler(void)
{
//Stop grabbing and processing
emit stopGrabbing();
emit stopProcessing();
/*Wait for the capture thread to terminate. The destructor of CCamera Handler might be called on application close. Therefore it is important to wait for QThreads to terminate. Else the application might close before threads get deleted*/
m_CameraCaptureThread->exit();
m_CameraCaptureThread->wait();
//Wait for the processing thread to terminate
m_ProcessingThread->exit();
m_CameraCaptureThread->wait();
qDebug() << "CCameraHandler deleted";
}

2 个答案:

答案 0 :(得分:1)

必须销毁已移动到另一个线程的对象:

  1. 来自线程本身,或

  2. 在之后的任何线程中,线程本身被破坏。

  3. 警告:QThread在你停止之前被破坏是不安全的。要安全地执行仅运行事件循环的线程,您应该使用以下子类:

    class Thread : public QThread {
      using QThread::run; // final
    public:
      Thread(QObject * parent = 0) : QThread(parent) {}
      ~Thread() { quit(); wait(); }
    };
    

    鉴于这样一个类,从GUI线程中被破坏,你只需要在破坏移动到线程的任何对象之前破坏它。当然,根本不需要将这些对象保存为指针,但无论是直接保存还是作为指针,下面的代码都会起作用。

    class Foo : public Bar {
      CCameraCapture m_CameraCapture;
      CImageProcessor m_ImageProcessor;
      Thread m_CameraCaptureThread;
      Thread m_ProcessingThread;
      ...
    }
    

    当类被破坏时,按顺序发生以下情况:

    1. ~Foo()身体跑(可能是空的)。
    2. ...部分的成员(如果有的话)按照与声明相反的顺序被破坏。
    3. m_ProcessingThread.~Thread运行,然后是超类析构函数(~QThread,最后是~QObject)。移动到该线程的任何对象现在都是无线的。
    4. m_CameraCaptureThread.~Thread运行,然后是超类析构函数。移动到该线程的任何对象现在都是无线的。
    5. m_ImageProcessor析构函数运行。作为一个无线对象,破坏对任何线程都是安全的。
    6. m_CameraCapture析构函数运行。作为一个无线对象,破坏对任何线程都是安全的。
    7. 如果您使用QScopedPointer<...>来保存这些实例,那么事情将完全相同,只是每个对象的破坏都将包含在~QScopedPointer<...>的正文中。

      请注意,使用甚至原始指针来保存这些实例是一种过早的悲观:你浪费了一点堆,并且由于额外的间接层,对实例的访问速度有点慢。孤立的东西可能不会发挥重要作用,但如果有成千上万的对象都以这种方式编码,那么事情可能会增加。

      除非绝对必要,否则不要在单个堆块中分配类成员。

答案 1 :(得分:0)

问题是你从无UI线程做一些UI的东西。 很难确切地说出问题的确切位置,因为您没有提供有关CCameraCapture的信息。

我怀疑在捕获帧后你在标签上设置一个像素图(显示帧)而不是发出一个带有新帧的信号,并将此信号与UI元素的相应插槽连接。所以我认为范围指针,信号和插槽与你的问题无关,问题是你没有在需要的地方使用信号槽机制。