Qt5のマルチスレッドを本当に理解する:マルチスレッドコンソールプログラムを作成し(movetothreadメソッドを使用)、BaiduNetdiskのダウンロードアドレスを添付します

Qtマルチスレッディングアプリケーションの例に関するオンライン記事はたくさんありますが、Qtマルチスレッディングの原理を実際に説明しているものはほとんどありません。その後、私はZhihuの「地面にワンチャンガオビル」という名前の著者によって書かれた2件の記事があったことが判明:
「QtのマルチスレッドプログラミングQThreadクラスのドアノック」https://zhuanlan.zhihu.com/p/53270619
「Qtの中をマルチスレッドテクノロジー」https://zhuanlan.zhihu.com/p/52612180に
加えて、公式のドキュメントとルーチンは、基本的にそれを理解し、公式のルーチンに基づいて変更しましたが、Qtから学ぶこともできます者リファレンス。この記事は、上記の2つの記事の内容の一部にも言及しています。政府が推奨する2番目のメソッド(moveToThread)にマルチスレッドを実装します。

キーコンセプト

最初にいくつかの重要なポイントについてお話ししましょう。詳細な理論については、上記の2つの記事を参照してください。

(1)QThreadクラスはQObjectから継承します。1QThreadインスタンスハイパーバイザーのスレッド。QThreadの実行はrun()から始まります。run関数は、デフォルトでexec()を呼び出して、イベントループを実装します。独自のコード実装をrun関数に配置すると、run関数が肥大化し、保守が困難になり、オブジェクト指向の開発に役立ちません。run関数を書き直すときに、run()関数でexec()を呼び出すのを忘れた場合、イベントループは発生せず、実行が実行された後にスレッドが終了します。

(2)QObjectのmoveToThread()関数は、クラス構造を破壊することなく、新しいスレッドで実行できます。QObject :: moveToThread()を使用して、カスタムクラスオブジェクト(ワーカークラス)をスレッドコンテナー(QThreadクラスのインスタンス)に移動して使用できます。Workerクラスへのアクセスと制御は、スレッドコンテナのメソッドとQtのシグナルスロットメカニズムによって実現されます。

ここに写真の説明を挿入

(3)コントローラーオブジェクトとワーカーオブジェクトはどのスレッドにありますか?「作成した場所が属する」というフレーズは、どこにでも当てはまります。moveToThread()関数は変更することですスロット機能指定されたスレッドで呼び出されます。つまり、コントローラーオブジェクトとワーカーオブジェクトはメインスレッドに。コントローラにバインドされているものを除くスロット機能(スロット機能本体で呼び出される機能を含む)さらに、残りのワーカー機能もメインスレッド実行中。

目的

この実験では、単純なコマンド対話型端末(whileループ)がmain関数に実装されました。ユーザーはコマンドを送信して、各スレッドの開始、停止、およびチェックを制御できます。2つのスレッドコンテナー(controller1とcontroller2)、各スレッドで実行する必要のある特定のことは、on_doSomething()関数によって実装されます(この実験では、実行することは、変数カウントを1ずつ累積し続けることです)。

実験手順

(1)QObjectに基づいてサブクラスWorkerを作成します。このクラスにはメンバー関数on_doSomething()があります。この関数で実行されているコードに非常に時間がかかる場合。したがって、このクラスオブジェクトを新しいスレッドに「移動」する必要があります。ここで、on_doSomethingをWorkerloop関数のwhile()に入れます。startWorker関数を実行して、ループ本体を開始します。このようにして、「時間のかかる作業」は新しいスレッドで実行されます。Workerloopのループ本体は、メンバー変数isStopの値に従って停止するかどうかを決定します。
Workerクラスには、外部コマンドを実行するためのスロット関数と、結果を外部に送信するための信号が必要です。メインスレッドには、startWorker関数を関連付けてトリガーするための信号信号があります(信号スロットメカニズムはisStopの値を変更するために使用されませんが、closeWorker関数はメインスレッドで直接呼び出されます)。同時に、Workerクラスには操作の結果を外部に送信するための信号信号(resultReady)があります(この実験では、メイン関数にwhile()コマンドの相互作用があるため、操作中にリアルタイムで表示することはできず、コマンドの相互作用の最後にのみ表示できます。表示後なので使用しません)。次のコードなど:

#ifndef WORKER_H
#define WORKER_H
#include <QObject>

class Worker : public QObject
{
    
    
    Q_OBJECT
public:
    explicit Worker( QString name,QObject *parent = nullptr);
signals:
    void resultReady(const QString &str);//向外界发送结果,本实验中没有使用
public slots:
    void startWorker();
    void closeWorker();
private:
    void Workerloop();//事件循环
    void on_doSomething();//耗时操作
private:
    bool isStop;
    int count;
    QString m_name;
};
#endif // WORKER_H

異なるスレッドでの実行を反映するために、各関数で現在のスレッドIDを出力します。

#include "worker.h"
#include <QDebug>
#include <QThread>

Worker::Worker(QString name,QObject *parent) : QObject(parent)
{
    
    
    m_name =  name;
    qDebug()<<"I'm "<< m_name <<" initialized in thread: "<<QThread::currentThreadId();
    isStop=false;
    count = 0;
}
void Worker::startWorker()
{
    
    
    qDebug()<<"I'm "<< m_name <<" startWorker in thread: "<<QThread::currentThreadId();
    isStop = false;
    Workerloop();
}
void Worker::closeWorker()
{
    
    
     qDebug()<<"I'm "<< m_name <<" closeWorker in thread: "<<QThread::currentThreadId();
    isStop = true;
}
void Worker::Workerloop()
{
    
    
     qDebug()<<"I'm "<< m_name <<" Workerloop in thread: "<<QThread::currentThreadId();
    while(!isStop)
    {
    
    
        on_doSomething();
        QThread::sleep(1);
    }
}
void Worker::on_doSomething()
{
    
    
    count++;
    qDebug()<<"I'm "<< m_name <<" working in thread: "<<QThread::currentThreadId();
 /*   QString workmsg;
    workmsg.sprintf("conut=: %d",count);
    emit resultReady(workmsg);*/
}

(2)スレッドの開始と停止を制御するQObjectベースのControllerクラスを作成します。ワーカークラスを開始するための同様の信号を「外部」としてControllerクラスで設計できますが、代わりにQThread :: started()信号が使用されます。on_receivResult()スロット関数は、新しいスレッドの実行結果を受け取るために使用されます(使用されません)。コントローラスロットはワーカー信号に対応します。

#ifndef CONTROLLER_H
#define CONTROLLER_H

#include <QObject>
#include <QThread>
#include "worker.h"

class Controller : public QObject
{
    
    
    Q_OBJECT
public:
    explicit Controller(int ctrlId,QString workname,QObject *parent = nullptr);
    ~Controller();
    void start();
    void stop();
signals:    

public slots:
    void on_receiveResult(const QString &str);
public:
bool isRunning;
private:
    QThread m_workThread;//线程容器。直接实例化,需要在析构函数中quit()+wait()
    Worker *m_worker;//线程中的对象 new在堆上,需用deleteLater().
    int m_ctrlId;
    QString m_workname;
};
#endif // CONTROLLER_H

QThreadクラスのインスタンスは、start()関数によって発行されたstartedシグナルを呼び出すことにより、新しいスレッドで実行する関数を開始できます。
通常の終了スレッドの本質は、イベントループを終了することです。つまり、exit(int returnCode = 0)関数を実行します。戻り値0は成功を意味し、その他のゼロ以外の値は異常を意味します。quit()関数はexit(0)と同等です。スレッドが終了した後、finished()シグナルが発行されます。

#include "controller.h"
#include <QDebug>
Controller::Controller(int ctrlId,QString workname,QObject *parent) : QObject(parent)
{
    
    
    m_ctrlId = ctrlId;
    m_workname = workname;
    qDebug()<<"Controller is running in main thread: "<<QThread::currentThreadId();
    isRunning = false;
    m_worker = new Worker(workname);
    m_worker->moveToThread(&m_workThread);

    connect(&m_workThread,&QThread::started,m_worker,&Worker::startWorker);
    connect(&m_workThread,&QThread::finished,m_worker,&QObject::deleteLater);
    connect(m_worker,&Worker::resultReady,this,&Controller::on_receiveResult);
}
Controller::~Controller()
{
    
    
	if(m_workThread.isRunning())
    {
    
    
    	m_worker->closeWorker();
        m_workThread.quit();
        m_workThread.wait();
     }
}
void Controller::start()
{
    
      
    m_workThread.start();
    isRunning = true;
}
void Controller::stop()
{
    
    
    if(m_workThread.isRunning())
    {
    
    
        m_worker->closeWorker();
        m_workThread.quit();
        m_workThread.wait();
        isRunning = false;
    }
}
void Controller::on_receiveResult(const QString &str)
{
    
    
    qDebug()<< str;
}

Controller :: 〜Controller()デストラクタが実行されると、m_workThreadオブジェクトが破棄されます。ただし、実行中のQThreadを削除すると(つまり、isFinished()はfalseを返します)、プログラムがクラッシュします。したがって、m_workThread.quit()とm_workThread.wait();を増やします。
(3)main関数で、Id番号がThrd0とThrd1であるControllerクラスの2つのインスタンスを作成します。ControllerクラスのWorkerクラスの初期名は次のとおりです。 "トムとジェリー"。
主な機能には、使用端末の対話を開始しながら、キーボードによって入力されたコマンドワードを監視するためにQTextStreamを使用する:
出口出口端末相互作用は、
スレッド1開始START1
START2開始スレッド2
1ストップ1出口スレッドを
ストップ2出口スレッド2
、スレッド1が実行されているかどうかCHECK1チェック
CHECK2チェックスレッドを2実行するかどうか

#include <QCoreApplication>
#include <QThread>
#include <QDebug>
#include <controller.h>

enum CTRID{
    
    
    Thrd0,
    Thrd1
};
int main(int argc, char *argv[])
{
    
    
    QCoreApplication a(argc, argv);
    qDebug()<<"The main threadID is: "<< QThread::currentThreadId();

    QString workername1("Tom");
    QString workername2("Jerry");
    Controller controller1(Thrd0,workername1);
    Controller controller2(Thrd1,workername2);

    qDebug()<<"you can enter the following commands by tapping the keyboard:\n";
    qDebug()<<"\tstart1\n\tstart2\n\tstop1\n\tstop2\n\tcheck1\n\tcheck2\n\teixt\n";

    QTextStream qin(stdin);
    QString cmd;
    while(1)
    {
    
    
        qin>>cmd;
        qDebug()<<"I'm running in main: "<<QThread::currentThreadId();
        qDebug()<<"receive:"<<cmd<<endl;

        if(cmd=="exit")
         {
    
    
            controller1.stop();
            controller2.stop();
            qDebug()<<"main had exited.you can close this console. "<<endl;
            break;
        }
        if(cmd=="check1")
            qDebug()<<"controller1 status is: "<<controller1.isRunning<<endl;
        if(cmd=="check2")
            qDebug()<<"controller2 status is: "<<controller2.isRunning<<endl;
        if(cmd=="start1")
        {
    
    
            controller1.start();
            qDebug()<<"controller1 is started"<<endl;
        }
        if(cmd=="start2")
        {
    
    
            controller2.start();
            qDebug()<<"controller2 is started"<<endl;
        }
        if(controller1.isRunning==true)
        {
    
    
            if(cmd=="stop1")
            {
    
    
                controller1.stop();
                qDebug()<<"controller1 is stoped"<<endl;
            }
        }
        if(controller2.isRunning==true)
        {
    
    
            if(cmd=="stop2")
            {
    
    
                controller2.stop();
                qDebug()<<"controller2 is stoped"<<endl;
            }
        }
    }
    return a.exec();
}

実行効果を確認します。
ここに写真の説明を挿入
起動後、コントローラーのインスタンス化とワーカーのインスタンス化はメイン関数で完了します。したがって、コントローラー1、コントローラー2、ワーカー1、およびワーカー2はすべてメインスレッド0x2524に存在します。
ここに写真の説明を挿入
「start1」コマンドと「start2」コマンドをそれぞれ入力すると、2つのスレッドのワーカーがそれぞれ2つのスレッド0x20e0と0x1cacで動作していることがわかります。
「stop1」コマンドと「stop2」コマンドをそれぞれ入力して、2つのスレッドを停止します。
注:スレッドを停止した後、start1とstart2を再度入力します。スレッドは開始できますが、コントローラーはコンストラクターを実行できず、ワーカーも開始できないため、情報を出力できません。

ソースコードがアップロードされ、無料でダウンロードできます。
https://download.csdn.net/download/SmartTiger_CSL/12158072
元々、このソースコードリソースはすべての人が無料でダウンロードできるものでしたが、CSDNプラットフォームはダウンロードポイントが必要になるように変更する必要がありました。したがって、BaiduNetdiskの無料ダウンロードアドレスは次のとおりです。
リンク1:https
://pan.baidu.com/s/1TWvURe7syAVBSaQd8EpTMg抽出コード:6d7z
ここに写真の説明を挿入
リンク2:https
://pan.baidu.com/s/1tq5oUv1e09VUCxEg1C4xMQ抽出コード:qm4g

おすすめ

転載: blog.csdn.net/SmartTiger_CSL/article/details/104324333
おすすめ