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;
*/

出力はまったく同じです!印刷後しばらくの間だけ残る

スレッドはまだ実行中ですか?!!!!

誰かがこれで私を助けてくれますか? どうもありがとう。すべてのコードをコピーして、自分でテストするだけです。


-------------------------元の質問、悪い説明、無視してください------------------------ ---------------------------

フレームをクエリする関数が 1 つだけの 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() は 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);

プログラムがフリーズするだけです。

誰かがこれで私を助けることができますか、それは本当にこのquit()とfinished()について私を混乱させます...ありがとう!

4

2 に答える 2

4

stop_flag = 1を呼び出すと、video_thread_は現在の関数を終了します。

ProcessFrame

ProcessFrameが終了する前に、 emit stopProcess()を介してvideo_thread_でquitが呼び出されます。

ただし、quitはterminateとは異なり、terminateはいつでもスレッドを終了できます(ただし、安全ではありません)。quitはイベントループで機能します。スレッドにeventloopがない場合、quitは効果がありません。

qthreadがイベントループ内の次のイベントを実行する前に、quitによって設定できるフラグをチェックし、フラグがquitによって設定された後、イベントループ内の次のイベントを実行しないと思います。または、終了イベントがイベントループに挿入され、イベントループ内の次のイベントが終了する場合もあります。

stop_flag = 1の後、video_thread _-> waitを呼び出します。これにより、video_thread_がイベントループ内の次のイベントを実行するのをブロックするため、タイムアウトする前に終了は有効になりません。 「」すぐに実行されます。video_thread-> waitを呼び出す代わりに、currentThread()-> Sleep(some_enough_time)を呼び出すと、video_thread_が次のイベントを実行して終了する時間があります。

QThreadのQtドキュメントを読むことができます。待機は、スレッドを同期的に終了するために終了とともに使用されます。

==============================新しいコード================= ===============

あなたがするとき:

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

シグナルソースはmyThreadであり、スロットもmyThreadに属します。これは、オブジェクト「myThread」がメインスレッドで作成され、オブジェクトとしてメインスレッドに存在するためです。myThread-> thread()を呼び出してこれを確認できます。これにより、新しいスレッドの代わりにメインスレッドが返されます。

ただし、開始信号は新しいスレッド、つまりmyThreadが表すスレッドから発行されるため、接続はQt::QueuedConnectionになります。dowork()はメインスレッドのイベントキューに投稿され、メインスレッドのeventloopを実行するa.exec()の後にのみ実行されます。

他の2つの接続呼び出しにも同じことが起こり、すべてのスロットがメインスレッドのイベントループで実行されます。

最初に、myThread-> startが呼び出されると、新しいスレッドから開始が発行され、doworkがメインスレッドのイベントキューにポストされます。

a.exec()を呼び出す前は、実際には何も起こりません。したがって、プログラムはcinに進み、stopをtrueに設定してから、「スレッドはまだ実行中ですか?!!」と出力します。

2番目a.exec()が呼び出されると、doworkがメインスレッドで実行され、「startwork」が出力されます。停止フラグがすでに真であり、フィニッシュワーク信号がメインスレッドから発行されているため、何も実行されません。「finishdowork」と出力します。

3番目最後のステップでフィニッシュワークが発行され、終了スロットが直接呼び出されます。ただし、新しいスレッドが実際に終了する前に、メインスレッドにイベントが投稿されなくなるため、メインスレッドはすでにイベントキューを終了しています。アプリケーションは、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. スレッドが実行されていることをエコーする
  6. イベントループを実行

ポイント 3 のスレッドはすでに実行されており、それについて通知されています。myObj はメイン スレッドで作成され、(そこでイベントを処理するために) 他のスレッドに移動されないため、スレッドは他に何もせず、イベント ループをスピンするだけです。同時に、myObj の dowork() を実行したいというイベントがメイン スレッドに投稿されます。最後にステップ 6 に進み、イベント ループの実行を開始します。最初に検出されるのは、myObj で dowork() を呼び出す必要があるイベントです。

Qt スレッドとシグナルスロット接続がどのように機能するかを明確にするために、Qt ブログのこの記事を読むことをお勧めします。

簡単に修正するには、オブジェクト myObj を実行したくないスレッドに移動できます。

しかし、これを本当に正しくするには、QRunnable をサブクラス化し、QThread で実行したくないことを実行するメソッドを (再) 実装することで、スレッドが正しく終了するよりも、その仕事を実行できるようにする必要があると思います。それに参加できます。または、目標によっては、 QtConcurrent::run(aFunction)を使用するとさらに良い場合があります

于 2013-04-12T06:38:02.953 に答える