2012年1月5日木曜日

( Qt C++ )QMutexLockerを使ったスレッド間のアクセス制御


今日からコード部分をハイライトするように設定しました。明日に過去全ての記事をハイライト形式に変更する予定です。よって明日は記事の投稿が出来ないかもしれません。


はい、それではQMutexLockerを使ったスレッド間のアクセス制御をやっていきます。
やっていることは前回と同じです。今回は更に簡単になります。

QMutexLockerはQMutexを簡単に扱うためのクラスです。コンストラクタでQMutexを引数にし、すぐ自動的にロックします。そして、デストラクタで自動的にアンロックします。
つまり、いちいちlock(),unlock()などと書かなくていいわけです。(もちろん書くこともできます。この辺はリファレンスを見てください。)

(※なお、このやり方もQMutexを使っているので、一つのスレッドからしかアクセスできません。多くのスレッドからのアクセス(読込)が必要な場合などには処理が遅くなるので別の方法が必要となります。このあたりは後ほど記事にします。) 

サンプルは前回と同じものを使います。
そして、いつものようにQtCreaterの使用を前提とします。(QtCreaterなどの使い方は ”Qtをはじめよう" を見てください。)
なお、サンプルコードはincludeの部分は省略しております。動かない場合はまずinclude部を疑ってください。
ではコードを


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

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

private:
    QMutex mutex;//<-----------------ここ重要!!
    volatile bool stopped;//volatileは処理系の最適化の抑制の意味
};


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

void CustumThread::run()////QThreadのrunの再実装
{
    bool first = true;
    forever
    {
        {
            QMutexLocker locker(&mutex);//ロックが開始される
            if(stopped)
            {
                stopped = false;
                break;
            }

            if(first)
            {
                messageStr = "Thread Running!";
                first = false;
            }
        }//ここを抜けるとQMutexLockerが自動アンロックする。
    }
    QMutexLocker locker(&mutex);//ロック開始。関数が終わると自動でアンロック
    messageStr = "Thread Stop!";
}

void CustumThread::stop()
{
    QMutexLocker locker(&mutex);//ロック開始。関数が終わると自動でアンロック
    stopped = true;
}
はい簡単ですね。ヘッダ部は前回と変わりなしです。.cppではrun()、stop()の部分がQMutexLockerで変更されています。いちいちlock(), unlock()を書かなくていいのでコードがスッキリしています。

これらの実行は前回と同じように


(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);//現在のスレッドの状態をラベルに表示
}


のようにします。実行部の説明は前々回と同じなので省略します。 実行結果ももちろん前回と同じです。

 

以上です。