2013年12月27日金曜日

QMutexについて

QMutexの使い方を調べたのでメモ

QMutex

QMutexはlockとunlockの間変数へのアクセスを禁止出来る。


        //変数への操作をロック
        mutex.lock();
        //読み出し
        QVector<int> out=data;
        //ロック解除
        mutex.unlock();

これだけだと、lock(),unlock()分処理が遅くなるだけで、何が面白いのか良く分からない、
        //変数への操作をロック
        mutex.lock();
        //読み出し
        data=set;
        //ロック解除
        mutex.unlock();
書き込み時にも使う事で、アトミックなデータアクセスが可能になる。
もしQVector<int> data;に変更があった時、別スレッドで読みだそうとすると。
mutex.unlock()が呼び出されるまで、読み出しが後回しにされる、
なので途中の中途半端な値にアクセスしてしまう事を回避できる。


サンプルコード
マルチスレッドで、同じクラスの配列に書き込みを行う、
各スレッドは、 isData()で配列を受け取り。
指定された値で全要素を埋めた後に、setData()で書き戻す。
0.1秒毎に、配列の要素が全て一致しているかチェックし。表示、全要素一致ならtrue、一致しないならfalse

MutexTest.pro

#-------------------------------------------------
#
# Project created by QtCreator 2013-12-27T13:41:25
#
#-------------------------------------------------

QT       += core
QMAKE_CXXFLAGS += -std=c++11

QT       -= gui

TARGET = MutexTest
CONFIG   += console
CONFIG   -= app_bundle

TEMPLATE = app


SOURCES += main.cpp \
    manageobject.cpp

HEADERS += \
    arraycalc.h \
    manageobject.h


arraycalc.h

#ifndef ARRAYCALC_H
#define ARRAYCALC_H
#include <QVector>
#include <QObject>
#include <memory>
#include <QMutex>

//データ保持
class Data
{
public:
    Data() {}
    QVector<int> isData(){
        //変数への操作をロック
        mutex.lock();
        //読み出し
        QVector<int> out=data;
        //ロック解除
        mutex.unlock();
        return out;
    }

    void setData(QVector<int> set){
        //変数への操作をロック
        mutex.lock();
        //書き込み
        data=set;
        //ロック解除
        mutex.unlock();
    }

private:
    QVector<int> data=QVector<int>(10000);
    QMutex mutex;
};



//配列操作クラス
class ArrayCalc :public QObject
{
    Q_OBJECT
public:
    //コンストラクタ
    explicit ArrayCalc(QObject *parent = 0):QObject(parent){}
    //データのセット
    void setData(std::shared_ptr<Data> setdata,const int setnum){
        data=setdata;
        num=setnum;
    }

signals:
    //処理終了のシグナル
    void WorkEnd();
public slots:
   //処理
    void doWork(){
        if(data){
            //配列の取得
            auto array=data->isData();
            if(array.count()>0){
                //設定値で配列を埋める
                array.fill(num);
            }
            //変更した配列の適用
            data->setData(array);
        }
        WorkEnd();
    }
private:
    std::shared_ptr<Data> data;
    int num;
};



#endif // ARRAYCALC_H


manageobject.h

#ifndef MANAGEOBJECT_H
#define MANAGEOBJECT_H

#include <QObject>
#include <QThread>
#include "arraycalc.h"
#include <memory>
#include <QDebug>
#include <QTimer>

//実行スレッドの管理クラス
class ManageObject : public QObject
{
    Q_OBJECT
public:
    explicit ManageObject(QObject *parent = 0);
private:
    //実行スレッド
    QThread *thread1;
    QThread *thread2;
    //処理スレッド
    ArrayCalc calc1;
    ArrayCalc calc2;
    //処理データ
    std::shared_ptr<Data> data;
    //タイマー
    QTimer *timer;
signals:
    void start1();
    void start2();
public slots:
    void finish1();
    void finish2();
    void on_timer();
private:
    void print();
   // void on_timer();
};

#endif // MANAGEOBJECT_H


manageobject.cpp

#include "manageobject.h"

ManageObject::ManageObject(QObject *parent) :
    QObject(parent)
{
    data=std::make_shared<Data>();
    //スレッド生成
    thread1=new QThread(this);
    thread2=new QThread(this);
    //処理データ設定
    calc1.setData(data,10);
    calc2.setData(data,-9);
    //親オブジェクトのリセット
    calc1.setParent(nullptr);
    calc2.setParent(nullptr);
    //スレッド移動
    calc1.moveToThread(thread1);
    calc2.moveToThread(thread2);
    //接続
    //処理の開始
    connect(this,SIGNAL(start1()),&calc1,SLOT(doWork()));
    connect(this,SIGNAL(start1()),&calc2,SLOT(doWork()));
    //処理終了
    connect(&calc1,SIGNAL(WorkEnd()),this,SLOT(finish1()));
    connect(&calc2,SIGNAL(WorkEnd()),this,SLOT(finish2()));
    //スレッド実行
    thread1->start();
    thread2->start();
    //シグナル呼び出し
    start2();
    start1();
    //表示タイマー設定
    timer=new QTimer(this);
    connect(timer,SIGNAL(timeout()),this,SLOT(on_timer()));
    timer->start(100);
}


//処理終了の通知
void ManageObject::finish1()
{
    start1();
}

void ManageObject::finish2()
{
    start2();
}

//タイマー起動
void ManageObject::on_timer()
{
    print();
}

//表示
void ManageObject::print()
{
    auto array=data->isData();
    if(array.count()>0){
        //配列の数字が全て一致しているか調べる falseなら競合発生
        auto chack=[](QVector<int> array){
            const int setnum=array[0];
            for(auto elem:array){
                if(setnum!=elem){
                    return false;
                }
            }
            return true;
        };
        qDebug() << chack(array) << array[0];
    }
}


main.cpp

#include <QCoreApplication>
#include <arraycalc.h>
#include <manageobject.h>

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);
    ManageObject manage;
    return a.exec();
}



このサンプルコードは、スレッドから終了シグナルを受け取とるとすぐ再度スレッドを実行する。
そのためCPU負荷が高い、



0 件のコメント:

コメントを投稿