QThread何时退出()或完成()?

时间:2012-12-20 06:08:15

标签: qt qthread

我是Qt的新手,只想在Qt GUI中显示视频。除了处理QThread的一些细节之外,我基本上都弄明白了,这真的很烦人。

我将我的问题重新形成一个更简单的程序,希望它能更好地解释

首先我定义了这个QObject类

#include <QObject>
#include <QDebug>
class myObject : public QObject
{
    Q_OBJECT
public:
    explicit myObject(QObject *parent = 0);
    bool stop;

signals:
    void finishWork();

public slots:
    void dowork();
    void onfinishThread();

};

myObject::myObject(QObject *parent) :
QObject(parent)
{
    stop = true;
}

void myObject::dowork(){
    qDebug()<<"start working!";
    while(!stop){
        qDebug()<<"working...";
    }
    emit finishWork();
    qDebug()<<"finish do work!";
}
void myObject::onfinishThread(){
    qDebug()<<"thread is finished!";
}

然后是主要功能

#include <QCoreApplication>
#include <QThread>

#include <iostream>
#include <windows.h>

#include "myobject.h"

using namespace std;
int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);

    myObject* myObj = new myObject();
    QThread* myThread = new QThread;

    myThread->connect(myObj, SIGNAL(finishWork()), SLOT(quit()));
    myObj->connect(myThread, SIGNAL(started()), SLOT(dowork()));
    myObj->connect(myThread, SIGNAL(finished()), SLOT(onfinishThread()));
    myObj->moveToThread(myThread);

    myObj->stop = false;

    myThread->start();

    cout<<"Press ENTER to continue....."<<endl<<endl;
    cin.ignore(1);

    myObj->stop = true;
    Sleep(10);

    if(myThread->isRunning()){
        qDebug()<<"The thread is still running?!!!";
    }

/*
    myObj->stop = false;
    Sleep(1000);
    myThread->start();
    myObj->stop = true;
*/

    myObj->deleteLater();
    myThread->deleteLater();
    return a.exec();
}

正如你所看到的,我甚至使用cin尝试让 dowork()先运行,但它根本不起作用,输出是

output

所以我对QThread的调度工作方式感到困惑......

此外,如果取消注释部分

/*
    myObj->stop = false;
    Sleep(1000);
    myThread->start();
    myObj->stop = true;
*/

输出完全一样!印刷后只停留一段时间

  

线程还在运行吗?!!!

有人会帮我这个吗?非常感谢。您可以简单地复制所有代码并自行测试。


-------------------------原帖,不好解释,请忽略 ....--- -------------------------------------

我创建了一个videoObj类,只有一个函数来查询帧,该函数定义为:

void videoObj::ProcessFrame(){
bool getframe;
qDebug()<<"get in ProcessFrame";
while(!stop_flag){
    getframe = capture_.read(imgOriginal_);
    if(!getframe){
        qDebug()<<"Video End!";
        break;
    }
    cv::cvtColor(imgOriginal_, imgOriginal_, CV_BGR2RGB);
    QImage qimgOriginal((uchar*)imgOriginal_.data, imgOriginal_.cols, imgOriginal_.rows, imgOriginal_.step, QImage::Format_RGB888);

    emit imgProcessed(qimgOriginal);
    this->thread()->msleep(10);
    //qDebug()<<"processing frames";
}

emit stopProcess();
qDebug()<<"Stop thread";
}

基本上,上面的代码只是查询框架,只要有人发出

SIGNAL imgProcessed(qimgOriginal)

并且每当stop_flag设置为on时,停止while循环并发出

SIGNAL stopProcess()

我在MainWindow类中使用此类,以下是我在构造函数中定义连接的方法:

MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);

video_obj_ = new videoObj();
video_thread_ = new QThread;
this->connect(video_obj_, SIGNAL(imgProcessed(QImage)), SLOT(onImgProcssed(QImage))); \\this is for displaying the image
video_obj_->connect(video_thread_, SIGNAL(started()), SLOT(ProcessFrame()));
video_obj_->moveToThread(video_thread_);
video_thread_->connect(video_obj_, SIGNAL(stopProcess()), SLOT(quit()));
}

以上代码在帧查询中工作正常。我不明白的问题是,如果我在任何MainWiow成员函数中设置video_obj _-&gt; stop_flag,则videoObj类中的ProcessFrame()应该发出stopProcess()信号并触发video_thread_的quit(),然后该线程应该完成,即video_thread _-&gt; finished()为true。

但是,如果我这样做:

connect(video_thread_, SIGNAL(finished()), this, SLOT(onStopProcess())); //onStopProcess() see below

void MainWindow::on_btnStart_clicked()
{
video_obj_->stop_flag = 1;
this->thread()->msleep(10);

video_obj_->capture_.open(ui->lineEditVidoeAddress->text().toStdString());
//... something about videoCapture setting here, not related

video_obj_->capture_.set(CV_CAP_PROP_POS_FRAMES, 0);
video_obj_->stop_flag = 0;
this->thread()->msleep(10);
if(video_thread_->isRunning()){
    qDebug()<<"The thread is still running?!!!";
}
video_thread_->start();
}

void MainWindow::onStopProcess(){
    qDebug()<<"thread is finished";
}

它会给我输出:

Stop thread 
The thread is still running?!!!  
thread is finished 

这意味着触发quit()没有完成线程,或者没有触发quit()。

如果我使用:

video_thread_->wait(); //instead of video_thread_->wait(10);

程序将冻结。

是否有人可以帮助我,这真的让我对这个戒烟()和完成()感到困惑...... 谢谢!

2 个答案:

答案 0 :(得分:4)

因为当你调用stop_flag = 1时,video_thread_完成当前函数

ProcessFrame
ProcessFrame 完成之前

,在 video_thread _ 上通过 emit stopProcess()调用quit。

但是,quit与terminate不同,terminate可以随时退出线程(但不安全),退出与事件循环一起工作,如果线程没有eventloop,则退出无效。

我想在qthread执行eventloop中的下一个事件之前,它会检查一些标志,可以通过quit设置,标志由quit设置,然后它不会执行eventloop中的下一个事件。或者也可以是将一个quit事件插入到eventloop中,并且eventloop中的下一个事件将被退出。

stop_flag = 1 之后,您调用 video_thread _-&gt;等待,这将阻止video_thread_执行eventloop中的下一个事件,因此退出将不会然而,在超时之前的效果,打印“未完成的下一行?!!!”立即执行。而不是调用video_thread-&gt;等待,如果你调用 currentThread() - &gt; Sleep(some_enough_time),那么video_thread_将有时间执行下一个事件并退出。

你可以阅读QThread的Qt文档,wait用于终止同步终止线程。

==============================新代码============== ==================

当你这样做时:

myObj->connect(myThread, SIGNAL(started()), SLOT(dowork()));

信号源是myThread,插槽也属于myThread,因为对象“myThread”是在主线程中创建的,因此它作为对象存在于主线程中。您可以调用myThread-&gt; thread()来查看它,它将返回主线程而不是新线程。

但是,启动的信号是从新线程发出的,即myThread表示的线程,因此连接是Qt :: QueuedConnection。 dowork()发布在主线程的事件队列中,只有在执行主线程eventloop的a.exec()之后才会执行。

同样的事情发生在其他2个连接调用上,所有插槽都将在主线程的事件循环中执行。

首先当调用myThread-&gt; start时,从新线程发出开始,将dowork发布到主线程的事件队列中。

在调用a.exec()之前没有任何事情发生; 所以程序将继续cin然后将stop设置为true,然后打印“Thread仍在运行?!!”。

当调用a.exec()时,将在主线程中执行dowork并打印“start work”。没有做任何事,因为停止标志已经为真,并且从主线程发出终止信号,打印“完成工作”;

第三次完成最终步骤,并直接调用退出槽。但是,在新线程真正退出之前,主线程已经完成了事件队列,因为没有更多事件发布到主线程。应用程序退出而不等待quit()生效。

要测试这是真的,请不要修改任何代码,只需在

之后添加
emit finishWork();
currentThread()->sleep(1000);

你会看到“线程已经完成!”打印,因为这会让新线程有时间发出finished(),并且onfinishThread()将添加到主线程的事件队列中。

顺便说一句,你使用线程的方式看起来像java风格,这不是标准的qt方式。在处理qt线程之前,您可以阅读this

答案 1 :(得分:1)

这不是调度问题。

您在代码中执行的操作如下:

  1. 创建一个帖子。
  2. 然后此线程发出一个启动信号,运行slot dowork()
  3. 开始一个帖子。
  4. 等待用户输入
  5. 关于线程正在运行的回声
  6. 执行事件循环
  7. 在第3点线程已经运行并发出信号。因为myObj是在主线程中创建的,而不是移动到任何其他线程(在那里处理事件),所以你的线程现在不做任何其他事情,只是旋转事件循环。同时,告诉您要运行myObj的dowork()的事件发布在主线程上。最后,它来到第6步,你开始执行它的事件循环,它找到的第一件事是需要在myObj上调用dowork()的事件。

    为了向您明确Qt线程和信号槽连接的工作原理,我建议您阅读this article on Qt blog

    简单地修复它,你可以将你的对象myObj移动到你不想运行的线程。

    但为了使这真的正确,我打赌你真正想要的是继承QRunnable并(重新)实现它的run方法来做你不想在QThread中的东西,所以那个线程它是否有效,而不是正确完成,所以你可以加入它。或者,根据您的目标,使用QtConcurrent::run(aFunction)

    可能会更好