多线程Qt应用程序在退出时无法停止

5
我正在编写一个简单的Qt程序,用于从摄像头(使用OpenCV)获取视频流。我正在使用QThread对象循环捕获图像并将其馈送到MainWindow对象。这个功能正常工作。
问题是当我关闭应用程序(即按下“X”)时,相机捕获线程停止并且GUI消失。但程序在后台仍在运行。同时,在应用程序输出中我也得到了以下警告:

QThread: Destroyed while thread is still running.

如何在退出应用程序时完全停止应用程序? 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

摄像头线程

#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

该程序包含的代码比这更多,但我只包含了我认为与问题相关的代码。如果需要,我会发布其余部分。

检查除UI和相机线程之外是否有其他线程在运行。您正在使用的API可能会生成其他线程。 - Paul Sasik
我不确定如何检查这个... 你能解释一下吗? - birgersp
检查运行中的线程取决于您使用的集成开发环境(如果值得使用)。例如,这个搜索: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 但是,它仍然取决于您的IDE。我只是演示了一个在Eclipse上的样例视图。 - Paul Sasik
1个回答

3

您的代码存在一些重要问题。首先,在您的运行函数中,无止尽的for循环将导致您的线程永远不会停止,除非您自己管理线程终止,例如:

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类的析构函数中以某种方式退出线程,就像所述的那样。


感谢您的反馈,我非常需要它,因为正如您可能已经意识到的那样,我是C++和Qt编程的新手。 但有一件事仍然不清楚。当我可以使用公共void方法停止应用程序时,为什么应该在CameraThread中使用公共插槽?有什么优缺点? - birgersp
1
如果您使用一种方法来停止应用程序,那么您必须直接从Application调用它,例如ct->setFinished(true);,这是非常危险的。这是因为它们在两个不同的线程中,并且同时从两个线程访问变量会导致未定义的行为。当您使用信号/槽机制来停止线程时,您可以确保该变量仅从一个线程(此处为CameraThread)访问。永远不要直接调用另一个线程中对象的函数。 - Nejat
知道了,谢谢。关于主要问题;我按照你的建议在应用程序析构函数中添加了ct->stop代码。还在线程的析构函数中添加了QThread::exit()。但是当我关闭主窗口后,应用程序仍在运行... - birgersp
你在运行函数中检查终止条件了吗?你在析构函数中将终止条件设置为true并等待完成了吗? - Nejat
是的,当 while 循环结束时,run 函数也会结束。但应用程序线程会无限期地等待相机线程完成... http://pastebin.com/P2SA7YDD - birgersp

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接