2

我是 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()先运行,但它根本不起作用,输出为

输出

所以我真的很困惑 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 时,停止 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_->stop_flag,videoObj 类中的 ProcessFrame() 应该发出 stopProcess() 信号并触发 video_thread_ 的 quit(),然后线程应该完成,即 video_thread_->finished() 为真。

但是,如果我这样做:

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);

该程序将冻结。

有谁可以帮我解决这个问题,这让我对这个 quit() 和 finished() 感到很困惑......谢谢!

4

2 回答 2

4

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

ProcessFrame

ProcessFrame完成之前,通过发出 stopProcess()在vi​​deo_thread_上调用 quit 。

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

我猜在 qthread 执行事件循环中的下一个事件之前,它会检查一些标志,可以通过退出设置,如果标志由退出设置,那么它不会执行事件循环中的下一个事件。或者也可以是在eventloop中插入了一个quit事件,eventloop中的下一个事件会被退出。

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

您可以阅读 QThread 的 Qt 文档,wait 与 terminate 一起用于同步终止线程。

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

当你这样做时:

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

信号源是myThread,slot也属于myThread,因为对象“myThread”是在主线程中创建的,所以它作为一个对象存在于主线程中。你可以调用 myThread->thread() 来查看,它会返回主线程而不是新线程。

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

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

首先在调用 myThread->start 时从新线程发出 start,dowork 被发布到主线程的事件队列中。

在你调用 a.exec(); 之前什么都没有发生。所以程序将继续执行 cin 然后将 stop 设置为 true,然后打印“线程仍在运行?!!”。

其次当调用a.exec()时,dowork在主线程中执行,并打印“start work”。什么都不做,因为停止标志已经为真,并且从主线程发出完成信号,打印“完成工作”;

第三步finishwork发出的最后一步,直接调用quit slot。然而,在新线程真正退出之前,主线程已经完成了事件队列,因为没有更多的事件被发布到主线程。应用程序退出而不等待 quit() 生效。

要测试这是真的,不要修改任何代码,只需在后面添加

emit finishWork();
currentThread()->sleep(1000);

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

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

于 2012-12-20T06:31:29.917 回答
1

这不是调度问题。

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

  1. 创建一个线程。
  2. 然后这个线程发出一个它开始的信号,运行 slot dowork()
  3. 开始一个线程。
  4. 等待用户输入
  5. echo 关于线程正在运行
  6. 执行事件循环

在第 3 点,线程已经在运行并发出信号。因为 myObj 是在主线程中创建的,并且没有移动到任何其他线程(在那里处理事件),所以您的线程现在不做任何其他事情,而只是旋转事件循环。同时,告诉您要运行 myObj 的 dowork() 的事件发布在主线程上。最后到了第 6 步,你开始执行它的事件循环,它首先找到的是需要在 myObj 上调用 dowork() 的事件。

为了让您清楚 Qt 线程和信号槽连接是如何工作的,我建议您阅读Qt 博客上的这篇文章

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

但要做到这一点,我敢打赌,你真正想要的是继承QRunnable并(重新)实现它的 run 方法来做你在 QThread 中不需要的东西,这样线程就可以完成它的工作,而不是正确完成,所以你可以加入它。或者根据您的目标,使用QtConcurrent::run(aFunction)可能会更好

于 2013-04-12T06:38:02.953 回答