1

I am working on an application where I use QThread to capture frames of camera (OpenCV). I followed the approach described here and moved a worker to the 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()));

This code is part of the constructor of my camera handler class. If the main application is closed I want to exit all threads. Therefore, the destructor of my CCameraHandler is:

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

The exit Slot in my camera capture which is called by the signal exitThreads() is:

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

As one can see from the connection setup the emitted finished() signal will quit the event loop of the thread and call deleteLater() of the Worker and the Thread. The destructor of the worker which is called looks like:

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

The result is that the Destructor of CCameraCapture is called correctly - it appears only one time in the QDebug stream but at the end of CCameraCapture::~CCameraCapture(void) scope. I get an access violation error from OpenCVs opencv_highgui249d.dll. As I am only using:

cv::VideoCapture m_Cap;

in the class definition of CCameraCapture, the destruction of m_Cap must cause this error. At the moment I really do not know how to solve this issue. Any ideas?

Edit: The application should close when the main window is closed using

this->setAttribute(Qt::WA_DeleteOnClose);

and

CMainWindow::~CMainWindow(){
m_CameraHandler->deleteLater();
m_ImageWidget->deleteLater();
m_ProcessedImageWidget->deleteLater();  
emit windowClosed();
qDebug() << "CMainWindow deleted";
}
4
  • At what point is your QApplication destroyed? Commented Apr 3, 2014 at 8:40
  • The main application consists of a MainWindow and two other not parented windows which dislpay processed and unprocessed images from the camera. Now, I want to shut down the application when the main window is closed using the destructor I added to the edit section of the post. Commented Apr 3, 2014 at 10:20
  • 1
    Well, you need to ensure that your threads exit before the QApplication is destroyed. Commented Apr 3, 2014 at 12:47
  • Thanks, that was a good hint! I solved the problem now calling exit() and wait() in the destructor. I will summarize it in a final post. Commented Apr 3, 2014 at 13:06

2 Answers 2

1

If the main application is closed I want to exit all threads.

Without debugging this myself, it looks like a problem here is the emit in the destructor of CCameraHandler.

One reason this is problematic is that if the user closes the application and it quits the main event loop, (started with QApplication's call to exec), any objects that have had deleteLater called may not actually be deleted. In this case, I'm specifically looking at m_CameraCaptureThread.

If we walk through the event handling of signals / slots: -

  • QApplication::processEvents...
    • CCameraCapture::exitThread()
      • emit finished
        • QThread::quit
        • QThread::deleteLater

By calling deleteLater, an event is placed in the current thread's event queue to process the delete after the slot function has exited. This occurs when the event loop next processes events.

However, the application is going to quit, so the event loop does not run again and the call to deleteLater is not serviced.

If all objects are running in the same thread, then signal / slot connections are direct, which would be less of an issue. However, with multiple threads, the connections are queued.

I suggest changing the destructor so that you clean up without using an emit signal here and see if the problem still persists.

Sign up to request clarification or add additional context in comments.

14 Comments

Thanks, that was helpful but why would the destructor be called if not by deleteLater()? It is the first time the destructor is called, else I would get two times the message "CCameraCapture deleted." The QObject CCameraCapture is not parented and created on the heap as one can see above. Therefore, the only call to the destructor can be made by the deleteLater(). Anyhow, I will change the emitted exitThread signals to a direct call of exitThread. The point why I used signals ist that I read everywhere that I should communicate with threads via signal/slots.
Ok, I changed CCameraHandler to: CCameraHandler::~CCameraHandler(void) { m_CameraCapture->exitThread(); m_ImageProcessor->exitThread(); qDebug() << "CCameraHandler deleted"; } and now I do not get any error. The point is that the destructor of CCameraCapture is now never called - I set a breakpoint there and its never triggered but the finished() signal in CCameraCapture::exitThread() is emitted. So it seems that deleteLater() is never executed.
From your first comment "why would the destructor be called if not by deleteLater"...which destructor? I see you've changed your post to show that the destructor of CMainWindow calls deleteLater on various objects. I suggest that if you want to delete an object and you're in a function that has not been called by a signal / slot, then delete the object directly, especially if you're closing the app.
Ok, I tried now to directly delete the Objects by using delete m_CameraCapture. However, then I will get again the access violation error like before :/
You need to delete objects from the thread in which they're running. You can either get the second thread to delete the object and wait for the thread to quit before terminating, or move the objects back to the main thread before deleting them.
|
1

Finally solved the problem: The application terminated before the threads left their eventloop. The point that a camera capture thread usually never terminates makes it necessary to exit the capturing loop at some point. If this exit is triggered by closing the application one needs to exit the threads before the application closes. I follow this example (see above). However, as the loop never terminates one needs emit a signal from the main thread to terminate. If this is done on close of the application, the signal will not arrive in time. Therefore, I connected the finished() signal of QThread to the deleteLater() of the worker

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

The finished signal will be emitted on exit of the event loop and will delete the QThread and the worker. In the destructor of the class which sets up the QThread and the worker I now use

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

At first I left out the wait and the application still crashed. For me this solved the problem. Thanks for the help to figure out this solution.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.