Как избежать ошибок нарушения доступа Qt при использовании QThread?

0

Я работаю над приложением, где я использую QThread для захвата кадров камеры (OpenCV). Я выполнил описанный здесь подход и перевел работника в 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();        
}

Как видно из настройки соединения, излучаемый законченный() сигнал выйдет из цикла событий потока и вызовет 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";
}
  • 0
    В какой момент ваше приложение QApplication уничтожено?
  • 0
    Основное приложение состоит из MainWindow и двух других непаренных окон, которые отображают обработанные и необработанные изображения с камеры. Теперь я хочу закрыть приложение, когда главное окно закрыто, используя деструктор, который я добавил в раздел редактирования поста.
Показать ещё 2 комментария
Теги:
opencv
image-processing
multithreading
qt

2 ответа

1

Если основное приложение закрыто, я хочу выйти из всех потоков.

Не отлаживая это самостоятельно, похоже, что проблема заключается в испускании в деструкторе CCameraHandler.

Одной из причин, по которым это проблема, является то, что если пользователь закрывает приложение и завершает цикл основного события (запускается с вызовом QApplication для exec), любые объекты, которые имеют вызванный deleteLater, фактически не могут быть удалены. В этом случае я специально смотрю m_CameraCaptureThread.

Если мы пройдем через обработку событий/слотов:

  • QApplication :: processEvents...
    • CCameraCapture :: ExitThread()
      • испустить
        • QThread :: бросить курить
        • QThread :: deleteLater

Вызывая deleteLater, событие помещается в текущую очередь событий потока для обработки удаления после выхода функции слота. Это происходит, когда цикл события затем обрабатывает события.

Однако приложение перестанет работать, поэтому цикл событий не запускается снова, и вызов функции deleteLater не выполняется.

Если все объекты работают в одном потоке, то соединения с сигналом/слотом являются прямыми, что будет менее проблематичным. Однако с несколькими потоками соединения помещаются в очередь.

Я предлагаю изменить деструктор так, чтобы вы очистились, не используя здесь испускаемый сигнал, и посмотрите, сохраняется ли проблема.

  • 0
    Спасибо, это было полезно, но зачем вызывать деструктор, если не deleteLater ()? Это первый раз, когда вызывается деструктор, иначе я бы дважды получил сообщение «CCameraCapture удален». QObject CCameraCapture не является родителем и создается в куче, как видно выше. Следовательно, deleteLater () может сделать единственный вызов деструктору. Во всяком случае, я заменим излучаемые сигналы exitThread на прямой вызов exitThread. Смысл, по которому я использовал сигналы, заключается в том, что я везде читаю, что я должен общаться с потоками через сигнал / слоты.
  • 0
    Хорошо, я изменил CCameraHandler на: CCameraHandler :: ~ CCameraHandler (void) {m_CameraCapture-> exitThread (); m_ImageProcessor-> ExitThread (); qDebug () << "CCameraHandler удален"; } и теперь я не получаю никакой ошибки. Дело в том, что деструктор CCameraCapture теперь никогда не вызывается - я установил там точку останова, и она никогда не сработала, но в CCameraCapture :: exitThread () испускается сигнал finish (). Таким образом, кажется, что deleteLater () никогда не выполняется.
Показать ещё 12 комментариев
0

Наконец, решена проблема: приложение завершилось до того, как потоки покинули их eventloop. Точка, в которой поток захвата камеры обычно никогда не заканчивается, делает необходимым выйти из цикла захвата в какой-то момент. Если этот выход запускается при закрытии приложения, необходимо закрыть потоки до закрытия приложения. Я следую этому примеру (см. Выше). Однако, поскольку цикл никогда не заканчивается, необходимо испустить сигнал из основного потока для завершения. Если это будет сделано при закрытии приложения, сигнал не будет поступать вовремя. Поэтому я подключил обработанный() сигнал QThread к deleteLater() рабочего

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

Готовый сигнал будет выходить при выходе из цикла событий и удалит QThread и рабочий. В деструкторе класса, который устанавливает QThread и рабочий, я теперь использую

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

Сначала я оставил ожидание, и приложение все еще разбилось. Для меня это решило проблему. Спасибо за помощь, чтобы выяснить это решение.

Ещё вопросы

Сообщество Overcoder
Наверх
Меню