2015-02-01 3 views
5

Я пишу простую программу Qt для захвата видеопотока с камеры (с использованием OpenCV). Я использую объект QThread, который петлиет, захватывает изображения и подает их на объект MainWindow. Это работает так, как должно.Многопоточное приложение Qt не останавливается при выходе

Проблема заключается в том, что когда я закрываю приложение (т. Е. Нажимая «X»), камера захватывает поток, и gui исчезает. Но программа все еще работает в фоновом режиме. Я также получаю предупреждение в выводе приложения:

QThread: Разрушен, пока поток еще запущен.

Как я могу полностью прекратить приложение при выходе из него?

main.cpp

#include <QApplication> 
#include "application.h" 

using namespace cv; 

int main(int argc, char *argv[]) 
{ 
    QApplication a(argc, argv); 

    Application app; 
    app.init(); 

    return a.exec(); 
} 

application.h

#include "mainwindow.h" 
#include "camerathread.h" 
#include "mathandler.h" 
#include "tools.h" 
#include "opencv2/core/core.hpp" 

#ifndef APPLICATION 
#define APPLICATION 

class Application : public MatHandler{ 
    MainWindow w; 
    CameraThread ct; 
public: 
    Application() { 
     w.setFixedSize(800,600); 
    } 

    void init() { 
     ct.setMatHandler(this); 
     ct.start(); 
     w.show(); 
    } 

    void handleMat(cv::Mat mat) { 
     QImage qImage = toQImage(mat); 
     w.setImage(qImage); 
    } 
}; 

#endif // APPLICATION 

camerathread

#include <QThread> 
#include "mathandler.h" 
#include "opencv2/highgui/highgui.hpp" 

#ifndef CAMERATHREAD 
#define CAMERATHREAD 

class CameraThread : public QThread { 
    MatHandler *matHandler; 
public: 
    ~CameraThread() { 
    } 

    void setMatHandler(MatHandler *h) { 
     matHandler = h; 
    } 

private: void run() { 
     cv::VideoCapture vc(0); 

     if (vc.isOpened()) { 
      for(;;) { 
       cv::Mat img; 
       vc >> img; 
       matHandler->handleMat(img); 
      } 
     } 
    } 
}; 

#endif // CAMERATHREAD 

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

+0

Проверьте, есть ли другие потоки, помимо пользовательского интерфейса и камеры. Возможно, API-интерфейсы, которые вы используете, порождают другие потоки. –

+0

Я не уверен, как проверить это ... Не могли бы вы объяснить? – gromit190

+0

Проверка на работающие потоки зависит от используемой вами среды (если она стоит соли). Например, этот поиск: https://www.google.com/webhp?sourceid=chrome-instant&ion=1&espv=2&ie=UTF-8#q=how%20to%20see%20running%20threads%20in%20eclipse дал этот результат : http://help.eclipse.org/indigo/index.jsp?topic=%2Forg.eclipse.mat.ui.help%2Ftasks%2Fanalyzingthreads.html Но опять же, это зависит от вашей среды разработки. Я просто продемонстрировал образец/w Eclipse. –

ответ

3

Есть некоторые серьезные проблемы с вашим кодом. Прежде всего в вашем ведении функции нескончаемая цикл заставит ваш поток никогда не останавливаться, если не удается терминация нити вашей собственной личности, как:

while(!finishThread) 
{ 
    cv::Mat img; 
    vc >> img; 
    matHandler->handleMat(img); 
} 

Вы должны установить finishThread в true, когда приложение будет закрыто. Просто укажите слот, в котором вы устанавливаете значение для finishThread. Когда вы хотите завершить поток, испустите сигнал, который подключен к этому слоту, с значением true. После этого ждать нить, чтобы закончить должным образом в течение нескольких секунд и заставить его прекратить, если он не договорил:

emit setThreadFinished(true); //Tell the thread to finish 
if(!ct->wait(3000)) //Wait until it actually has terminated (max. 3 sec) 
{ 
    ct->terminate(); //Thread didn't exit in time, probably deadlocked, terminate it! 
    ct->wait(); //We have to wait again here! 
} 

Также вы не должны непосредственно вызывать handleMat функцию из другого потока. Это может привести к краху вашего приложения или привести к неопределенному поведению. Для этого используется механизм сигнала/слота. Подключите сигнал от вашего потока к этому слоту и выпустите сигнал по аргументу каждый раз, когда вы хотите его вызвать.

Другое дело, что вам лучше получить свой класс от QObject и использовать moveToThread. Вы можете сделать это в конструкторе класса:

th = new QThread(); 

this->setParent(0); 
this->moveToThread(th); 

QObject::connect(th,SIGNAL(started()),this,SLOT(OnStarted())); 
QObject::connect(th,SIGNAL(finished()),this,SLOT(OnFinished())); 

th->start(); 

инициализация и завершение задача должна быть сделаны в OnStarted() и OnFinished() слотах соответственно. У вас может быть рабочая функция, в которой вы запускаете повторяющуюся операцию.

Также в деструкторе вашего класса Application quit the thread каким-то образом, как указано.

+0

Спасибо за отзыв, это очень необходимо, как я (как вы, наверное, уже поняли) новичок в программировании на C++ и Qt. Но одно остается мне непонятным. Почему я должен использовать общедоступный слот в CameraThread, когда я могу использовать общедоступный метод void для остановки приложения? Каковы плюсы и минусы? – gromit190

+1

Если вы используете метод остановки приложения, тогда вам нужно прямо называть его как 'ct-> setFinished (true);' from 'Application', что действительно опасно. Это потому, что они находятся в двух разных потоках, и доступ к переменной из двух потоков одновременно приводит к неопределенному поведению. Когда вы используете механизм сигнала/слота для остановки потока, вы убедитесь, что переменная доступна только из одного потока (здесь «CameraThread»). Никогда не вызывайте функцию объекта, находящуюся в другом потоке. – Nejat

+0

Отмечено, спасибо. По основному вопросу; Я добавил код ct-> stop в деконструктор приложения, как вы предлагали. Также добавлен QThread :: exit() в деконструкторе Thread. Приложение все еще работает после закрытия главного окна ... – gromit190

Смежные вопросы