使用QThread时如何避免Qt访问冲突错误?

时间:2014-04-03 08:07:05

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

我正在开发一个应用程序,我使用QThread来捕获相机帧(OpenCV)。我按照方法described here将一名工人转移到了QThread:

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

//Connect error signal
QObject::connect(m_CameraCapture, SIGNAL(error(QString,QString)), this, SLOT(reportError(QString,QString)));
//Connect the finished signal of the worker class to the thread for quitting the loop
QObject::connect(m_CameraCapture, SIGNAL(finished()), m_CameraCaptureThread, SLOT(quit()));

//This connections guarantees that the *m_CVideoCapture is automatically deleted if the event loop of the thread is terminated. Therefore, m_CVideoCapture does not need to be released manually if the capturing process is stopped.
QObject::connect(m_CameraCaptureThread, SIGNAL(finished()), m_CameraCaptureThread, SLOT(deleteLater()));
QObject::connect(m_CameraCapture, SIGNAL(finished()), m_CameraCapture, SLOT(deleteLater()));
QObject::connect(this, SIGNAL(exitThreads()), m_CameraCapture, SLOT(exitThread()));

此代码是我的相机处理程序类的构造函数的一部分。如果主应用程序关闭,我想退出所有线程。因此,我的CCameraHandler的析构函数是:

CCameraHandler::~CCameraHandler(void)
{
    emit exitThreads();
    qDebug() << "CCameraHandler deleted";
}

我的相机捕获中的退出槽由信号exitThreads()调用:

void CCameraCapture::exitThread(){
    //Stop grabbing
    stopGrabbing();
    //Emit finished signal which should be connected to quit() of QThread and deleteLate of this class;
    emit finished();        
}

从连接设置可以看出,发出的finished()信号将退出线程的事件循环并调用Worker和Thread的deleteLater()。被调用的工作者的析构函数看起来像:

CCameraCapture::~CCameraCapture(void)
{
qDebug() << "CCameraCapture deleted";
}

结果是CCameraCapture的析构函数被正确调用 - 它在QDebug流中只出现一次,但在CCameraCapture :: ~CCameraCapture(void)范围结束时出现。我从OpenCVs opencv_highgui249d.dll收到访问冲突错误。因为我只使用:

cv::VideoCapture m_Cap;

在CCameraCapture的类定义中,m_Cap的破坏必然会导致此错误。目前我真的不知道如何解决这个问题。有什么想法吗?

编辑: 使用

关闭主窗口时应关闭应用程序
this->setAttribute(Qt::WA_DeleteOnClose);

CMainWindow::~CMainWindow(){
m_CameraHandler->deleteLater();
m_ImageWidget->deleteLater();
m_ProcessedImageWidget->deleteLater();  
emit windowClosed();
qDebug() << "CMainWindow deleted";
}

2 个答案:

答案 0 :(得分:1)

  

如果主应用程序已关闭,我想退出所有线程。

我自己没有调试,看起来这里的问题是CCameraHandler的析构函数中的emit。

这有问题的一个原因是,如果用户关闭应用程序并退出主事件循环(从QApplication调用exec开始),则任何已调用deleteLater的对象实际上可能不会被删除。在这种情况下,我特别关注m_CameraCaptureThread。

如果我们完成信号/插槽的事件处理: -

  • 的QApplication :: processEvents ...
    • CCameraCapture ::了ExitThread()
      • 发射完毕
        • 的QThread ::退出
        • 的QThread :: deleteLater

通过调用deleteLater,事件被放置在当前线程的事件队列中,以在插槽函数退出后处理删除。当事件循环接下来处理事件时会发生这种情况。

但是,应用程序将退出,因此事件循环不会再次运行,并且不会对deleteLater的调用进行服务。

如果所有对象都在同一个线程中运行,那么信号/插槽连接是直接的,这不是问题。但是,对于多个线程,连接会排队。

我建议更改析构函数,以便在不使用发射信号的情况下进行清理,看看问题是否仍然存在。

答案 1 :(得分:0)

最后解决了问题:应用程序在线程离开事件循环之前终止。摄像机捕获线程通常永远不会终止的点使得必须在某个时刻退出捕获循环。如果通过关闭应用程序触发此退出,则需要在应用程序关闭之前退出线程。我遵循这个example(见上文)。但是,由于循环永远不会终止,因此需要从主线程发出信号以终止。如果在应用程序结束时完成此操作,信号将无法及时到达。因此,我将QThread的finished()信号连接到worker

的deleteLater()
QObject::connect(m_CameraCaptureThread, SIGNAL(finished()), m_CameraCapture, SLOT(deleteLater()));
QObject::connect(m_CameraCaptureThread, SIGNAL(finished()), m_CameraCaptureThread, SLOT(deleteLater()));

完成的信号将在事件循环退出时发出,并将删除QThread和worker。在设置QThread的类的析构函数和我现在使用的工作者

CCameraHandler::~CCameraHandler(void)
{
    emit stopGrabbing();
    m_CameraCaptureThread->exit();
    m_CameraCaptureThread->wait();
    qDebug() << "CCameraHandler deleted";
}

起初我省略了等待,应用程序仍然崩溃。对我来说,这解决了这个问题。感谢您帮助找出这个解决方案。