2012年1月3日火曜日

( Qt C++ )QThreadのサブクラス化によるマルチスレッドの実現


今日はインクジェットプリンタにムカついた。もうね使ってないんだよ?使ってないのになんでインク空なんだよ!?本日5千円の出費。
プリンタ本体は確かに安い。だけどこれインク乾きすぎ。たかだか半年で全部なくなるって製品としてどうなの?電源切れると同時にインク噴出口の蓋がしまるとかさ、そういう機能を何でつけようとしないわけ?
次は絶対インクジェットプリンタは買わん。モノクロレーザーにする。



はい、それでは今日からマルチスレッドをやっていきます。
今回はQThreadのサブクラス化によるマルチスレッドをやっていきます。(リファレンス
サンプルはC++ GUI Programming with Qt4 381ページのものを少し改変し、使用します。
そして、いつものようにQtCreaterの使用を前提とします。(QtCreaterなどの使い方は ”Qtをはじめよう" を見てください。)
なお、サンプルコードはincludeの部分は省略しております。動かない場合はまずinclude部を疑ってください。

ではコードを

(custumthread.h)
class CustumThread : public QThread
{
    Q_OBJECT//マクロ
public:
    explicit CustumThread();//コンストラクタ
    void stop();//スレッドを止める
    QString messageStr;//スレッドが走っているかの状態メッセージ格納

protected:
    void run();//スレッドを始める関数(再実装)

private:
    volatile bool stopped;//volatileは処理系の最適化の抑制の意味
};

(custumthread.cpp)
CustumThread::CustumThread()//コンストラクタ
{
    messageStr = "Thread Stop!";
    stopped = false;
}

void CustumThread::run()//QThreadのrunの再実装
{
    bool first = true;
    while(!stopped)//スレッドがrun状態ならループという意味
    {
        if(first)//ループの最初の一回だけmessageStrを変更するという意味
        {
            messageStr = "Thread Running!";//run中なら
            first = false;
        }
    }
    stopped = false;
    messageStr = "Thread Stop!";
}

void CustumThread::stop()
{
    stopped = true;//スレッドをとめるためのフラグを立てる
}



はい簡単ですね。コメントのとおりです。run()は再実装です。スレッド開始されると呼び出されます。このrunが終わったときがスレッドの終了した時です。(リファレンス

これらの実行は以下のコードのようになります。


(mainwindow.h)
namespace Ui {
class MainWindow;
}

class MainWindow : public QMainWindow
{
    Q_OBJECT//マクロ
   
public:
    explicit MainWindow(QWidget *parent = 0);
    ~MainWindow();
   
private slots:
    void on_pushButton_clicked();//スレッド開始ボタン

    void on_pushButton_2_clicked();//スレッド停止ボタン

    void on_pushButton_3_clicked();//スレッドの状態表示ボタン

private:
    Ui::MainWindow *ui;//uiにはGUI部品類が記述されている。
    CustumThread threadA;//<-----------ここ重要。
};


(mainwindow.cpp)
MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)//コンストラクタ
{
    ui->setupUi(this);
}

MainWindow::~MainWindow()//デストラクタ
{
    delete ui;
}

void MainWindow::on_pushButton_clicked()//Startボタンクリック
{
    if(!threadA.isRunning())//スレッドが停止しているなら開始
   {
        threadA.start();
    }
}

void MainWindow::on_pushButton_2_clicked()//Stopボタンクリック
{
    if(threadA.isRunning())//スレッドが走ってるなら停止
   {
        threadA.stop();
        threadA.wait();//終わるまで待つ
   }
}

void MainWindow::on_pushButton_3_clicked()//状態表示ボタンクリック
{
    ui->label->setText(threadA.messageStr);//現在のスレッドの状態をラベルに表示
}


はい簡単ですね。理解に重要なのはヘッダのCustumThread宣言部とボタンクリックシグナルの部分(on_pushButtonの部分)だけです。一つ目のボタンを押すとthreadA.startでスレッドを開始します。二つ目のボタンはもしスレッドが開始されているならばthreadAをとめます。threadA.wait()は確実にスレッドが終わるのを待つために呼び出しています。(リファレンス) 三つ目のボタンはthreadAのフィールドのmessageStr(スレッドの状態の文字列格納)を呼び出し、それをラベルに表示させています。 実行すると以下のようになります。(下図はスタートを押した後に状態表示を押した時の場面です。)

以上です。