Qt基礎開発のQtマルチスレッドクラスQThreadとQtタイマクラスQTimerの詳細な方法と例

8708 ワード

Qtマルチスレッド
以前のプログラムは単一スレッドで実行されていましたが、次にマルチスレッドの導入を開始します.昔の一人が働いていたのに相当し、今は複数人で働いています.
Qtではマルチスレッドを使用する必要があります.これは、Qtアプリケーションがイベント駆動型であり、あるイベント処理関数の処理時間が長すぎると、他のイベントがタイムリーに処理されないためです.
Qtでは、スレッドを管理するためにQThreadを使用します.QThreadオブジェクトは、スレッドです.QThreadオブジェクトには、このスレッドのイベントを処理するためのメッセージシーケンスexec()関数もあります.
Qtマルチスレッドを実現するには2つの方法がある
1、Qt第一種類のスレッド作成方式
まずはQThreadを継承
書き換え虚関数QThread::run

[virtual protected] void QThread::run()
 /*
 *   QThread run        exec()    
 */

例:

#include 
#include 
#include 
class MyThread : public QThread
{
public:
 void run()
 {
  qDebug() << "QThread begin" << endl;
  qDebug() << "child thread" << QThread::currentThreadId() << endl;
  QThread::sleep(5);
  qDebug() << "QThread end" << endl;
  exec();
 }
};
​
int main(int argc, char** argv)
{
 QApplication app(argc, argv);
​
 MyThread thread;
 thread.start();
 qDebug() << "main thread" << QThread::currentThreadId() << endl;
 QThread::sleep(5);
 qDebug() << "main thread" << QThread::currentThreadId() << endl;
 thread.quit();
 qDebug() << "main thread thread.quit()" << endl;
 tread.wait();
 qDebug() << "main thread thread.wait()" << endl;
 return app.exec();
}

QThreadのquitを使用すると、スレッドのメッセージループを終了することができ、すぐに終了するのではなく、cpuの制御権がスレッドのexec()に返されるまで待つ必要がある場合があります.
一般にサブスレッドの終了時にはメインスレッドがリソースを回収する必要がある、QThreadのwaitを呼び出し、サブスレッドの終了を待ってからリソースを回収することができる.
2、Qt第二のスレッド作成方式
QObjectの継承
QThreadオブジェクトをインスタンス化
スロット関数を実現する.
QObjectサブクラスオブジェクトは、moveToThreadを介してスレッドQThreadオブジェクトに自己を配置する.
QThreadオブジェクトのstart関数起動スレッドを呼び出す
送信信号によってスロット関数をスレッドで実行する必要があり、送信信号はスレッドexec()メッセージキューに格納される.
例:
mywork.h

#ifndef MYWORK_H
#define MYWORK_H
#include 
#include 
class MyWork : public QObject
{
 Q_OBJECT
public slots:
 void workSlot()
 {
  qDebug() << "QThread begin" << endl;
  qDebug() << "child thread" << QThread::currentThreadId() << endl;
  QThread::sleep(5);
  qDebug() << "QThread end" << endl;
 }
};
#endif // MYWORK_H

widget.cpp

#include 
#include 
#include 
#include "mywork.h"
​
int main(int argc, char** argv)
{
 qDebug() << "main thread" << QThread::currentThreadId() << endl;
 QApplication app(argc, argv);
 QThread thread;
 MyWork work;
 work.moveToThread(&thread);
 QObject::connect(&thread, SIGNAL(started()), &work, SLOT(workSlot()));
 thread.start();
 
 QThread::sleep(6);
 qDebug() << "thread is runing" << thread.isRunning() << endl;
 thread.quit(); //  quit         ,       exec   
 thread.wait(); //   quit       wait       
 qDebug() << "thread is runing" << thread.isRunning() << endl;
​
 return app.exec();
}

特に注意しなければならないのは、
  • ラインスロット関数は、スレッドexec()への入力が完了し、送信信号によってスロット関数をスレッドで再実行することができる.スレッドexec()をquit()で終了することもできます.
  • QObject派生クラスオブジェクトは、moveToThreadを呼び出すために呼び出されます.プライマリ・スレッドの親オブジェクトの管理メモリを指定することはできません.
  • QWidgetのオブジェクトおよび派生クラスオブジェクトは、親が指定されていなくても、GUIマスタースレッドでのみ実行できます.moveToThreadを使用してサブスレッドに移動することはできません.

  • マルチスレッドオブジェクトメモリの解放
    QObjectオブジェクトがメモリオブジェクトを管理できない以上、スレッドオブジェクトを先に解放するのか、それともこのQObjectオブジェクトを先に解放するのか.
    QObjectをスレッドループから解放し(QObject::deleteLater関数を使用)、QThread::quit、QThread::wait.
    例:
    ​mywork.h
    
    #ifndef MYWORK_H
    #define MYWORK_H
    ​
    #include 
    #include 
    class MyWork : public QObject
    {
     Q_OBJECT
    public:
    ​
     ~MyWork() { qDebug() << __FUNCTION__ << endl; }
    public slots:
     void workSlot()
     {
      while(1)
      {
       qDebug() << "Work begin" << endl;
       QThread::sleep(5);
       qDebug() << "work end" << endl;
      }
     }
     void otherWorkSlot()
     {
      qDebug() << "otherWork begin" << endl;
      QThread::sleep(5);
      qDebug() << "otherWork end" << endl;
     }
    ​
    ​
    };
    ​
    #endif // MYWORK_H

    widget.h
    
    #ifndef WIDGET_H
    #define WIDGET_H
    ​
    #include 
    #include "mywork.h"
    ​
    class Widget : public QWidget
    {
     Q_OBJECT
    ​
    public:
     Widget(QWidget *parent = 0);
     ~Widget();
    private:
     QThread *_thread;
     MyWork * _myWork;
    };
    ​
    #endif // WIDGET_H

    widget.cpp
    
    #include "widget.h"
    #include 
    #include "mywork.h"
    #include 
    #include 
    ​
    Widget::Widget(QWidget *parent)
     : QWidget(parent)
    {
     _myWork = new MyWork;
     _thread = new QThread(this);
     _myWork->moveToThread(_thread);
     _thread->start();
    ​
     QPushButton *pb0 = new QPushButton("work", this);
     QPushButton *pb1 = new QPushButton("pb", this);
     QHBoxLayout *hBox = new QHBoxLayout(this);
     hBox->addWidget(pb0);
     hBox->addWidget(pb1);
     this->setLayout(hBox);
    ​
     /*                   */
     connect(pb0, SIGNAL(clicked()), _myWork, SLOT(workSlot()));
     connect(pb1, SIGNAL(clicked()), _myWork, SLOT(otherWorkSlot()));
    ​
     /*        */
     //connect(_thread, SIGNAL(finished()), _myWork, SLOT(deleteLater()));
    ​
    }
    ​
    Widget::~Widget()
    {
     _myWork->deleteLater(); //    QThread      
     _thread->quit();
     _thread->wait();
    }

    3、Qtスレッドの同期
    マルチスレッドは同時に1つのリソースにアクセスしています(例えば、複数のスレッドで操作可能な変数、関数など)、いったい誰がこのリソースを使用するかは問題です.大勢の人が同じケーキを奪い取るように、その中の1人が奪い取る可能性があります.ケーキがぼろぼろに奪われる可能性があります.マルチスレッドでは、これを競争冒険と言います.では、マルチスレッドで同期方法と呼ばれるケーキを受け取るために列に並ぶなど、一人一人を制約するルールを決める必要があります.
    同期は同時に行われるのではなく、秩序正しく行われることに注意してください.
    3.1、反発ロック
    Qtの反発ロックはQMutexであり、Qtベースクラスを継承せず、QMutexを使用して共有リソースをロックし、どのスレッドが鍵を奪うか、どのスレッドがこのリソースの使用権を持っているか、他のスレッドがこのスレッドがリソースを使用し終わって鍵を返すのを待ってから、鍵を奪う.
    例:
    
    QMutex mutex; //                  
    mutex.lock(); //              ,        ,           。
    mutex.unlock(); //   

    QMutex::lock関数はスレッドにロックの取得を待たせます.待たない場合は、関数の置き換えを使用します.
    
    bool QMutex::tryLock(int timeout = 0)
    /*
    *   int timeout:  timeout  ,           ,timeout 0 ,    。
    *    true      ,false     
    */

    ロックを解除するのを忘れることがあります.Qtはロックを管理するクラスQMutexLockerを提供しています.
    
    QMutex mutex;
    void func()
    {
     QMutexLocker locker(_mutex); //QMutexLocker         ,     QMutex  。 
    }

    以上の例では、func()実行が完了するとlockerが自動的に解放され、解放される前にロックが解除される.
    3.2、信号量
    反発ロックによって保護されたリソースは、同じ時点で1つのスレッドしか使用権を取得できません.複数のスレッドの同時アクセスを制限できるリソースもあります.この場合、信号量を使用できます.Qtにおける信号量はQSemaphoreである.
    
    QSemaphore sem(2) //       2
    sem.acquire(); //     0   ,           -1,       ,    
    semaphore.release(); //    +1

    4、QtタイマーQTimer
    タイマは、時間を隔てて信号を送信し、この信号を受信することによっていくつかのタイミングタスクを処理することができ、タイマが新しいスレッドを開いていないことに注意しなければならない.QtのタイマはQTimerがQObjectから継承する.
    QTimer::start()でタイマーを起動
    
    void start(int msec)
    /*
    *  msec   ,  timeout()  
    */

    リンク信号timeout()によって、いくつかのタイミングタスクを処理します.たとえば、次のようにします.
    
    #include "dialog.h"
    #include 
    #include 
    Dialog::Dialog(QWidget *parent)
     : QDialog(parent)
    {
     QTimer *timer = new QTimer(this);
     connect(timer, SIGNAL(timeout()), this, SLOT(doSomeThing()));
     timer->start(3000);
     
    }
    void Dialog::doSomeThing()
    {
     qDebug() << __FUNCTION__ << endl;
    }
    ​
    Dialog::~Dialog()
    {
    ​
    }

    ここでは、QtマルチスレッドクラスQThreadとQtタイマクラスQTimerの詳細な方法と例について説明します.Qtの開発に関する詳細は、次のリンクを参照してください.