web-dev-qa-db-ja.com

QMutexの使い方

私はQtの初心者で、Qtでマルチスレッドを探しています。
Qt Documents で学習したように、2つのスレッドに2つのクラスを定義しました。

_#include <QThread>
#include <QMutex>

class thread_a : public QThread
{
    Q_OBJECT
public:
    explicit thread_a(QObject *parent = 0);
    int counter;

protected:
    void run();
};
_

そしてCPPファイルで:

_#include "thread_a.h"

thread_a::thread_a(QObject *parent) :
    QThread(parent)
{
    counter=0;
}

void thread_a::run()
{
    counter++;
}
_

2番目のスレッドクラスは同じですが、run()メソッドに_counter--_が含まれています。
次に、この2つのスレッドを_main.ccp_から実行します。

今私の質問:
counterを使用して_thread_a_および_thread_b_でQMutexを共有するにはどうすればよいですか?

16
S.M.Mousavi

スレッド内にデータを置く代わりに、データをスレッドの外に移動して保護し、両方のスレッドからアクセスします。

以下は、あなたができることのスケッチです:

class Counter
{
  public:
    Counter():mMutex(),mCounter(0){}
    int inc()
    {
      QMutexLocker ml(&mMutex);
      return mCounter++;
    }
    int dec()
      QMutexLocker ml(&mMutex);
      return mCounter--;
    }
  private:
    QMutex mMutex;
    int mCounter;
    Q_DISABLE_COPY(Counter)
};

class ThreadA : public QThread
{
  public:
    ThreadA(Counter* ctr);
  /* ... */
};

class ThreadB : public QThread
{
  public:
    ThreadB(Counter* ctr);
  /* ... */
};

Counterの構成要素は、Wikipediaから Monitor と呼ばれることがよくあります(鉱山を強調):

並行プログラミングでは、モニターは、複数のスレッドで安全に使用されることを目的としたオブジェクトまたはモジュールです。モニターの特徴は、そのメソッドが相互に排他的に実行されることです。つまり、各時点で、最大1つのスレッドがそのメソッドのいずれかを実行している可能性があります。これの相互排除により、データ構造を更新する並列コードに関する推論と比較して、モニターの実装に関する推論が大幅に簡略化されます。

この特定のケースでは、より効率的な構成は QAtomicInt です。これは、特別なCPU命令の使用から原子性を獲得します。これは、他のスレッド構造の実装に使用できる低レベルのクラスです。


編集-完全な例

共有状態のスレッドを正しく使用することは簡単ではありません。キューに入れられた接続または他のメッセージベースのシステムでQt信号/スロットを使用することを検討してください。

または、Adaなどの他のプログラミング言語は、スレッドおよびモニター(保護オブジェクト)をネイティブ構成としてサポートします。

以下は完全に機能する例です。これはサンプルコードにすぎません。実際のコードではQTest::qSleepを使用しないでください。

objs.h

#ifndef OBJS_H
#define OBJS_H

#include <QtCore>

class Counter
{
    public:
        Counter(int init);
        int add(int v);
    private:
        QMutex mMutex;
        int mCounter;
        Q_DISABLE_COPY(Counter)
};

class CtrThread : public QThread
{
    Q_OBJECT
    public:
        CtrThread(Counter& c, int v);
        void stop();
    protected:
        virtual void run();
    private:
        bool keeprunning();
        Counter& mCtr;
        int mValue;
        bool mStop;
        QMutex mMutex;
};

#endif

objs.cpp

#include "objs.h"

Counter::Counter(int i):
    mMutex(),
    mCounter(i)
{}

int Counter::add(int v)
{
    QMutexLocker ml(&mMutex);
    return mCounter += v;
}

///////////////////////////////////////

CtrThread::CtrThread(Counter& c, int v):
    mCtr(c),
    mValue(v),
    mStop(false),
    mMutex()
{}

void CtrThread::stop()
{
    QMutexLocker ml(&mMutex);
    mStop = true;
}

void CtrThread::run()
{
    while(keeprunning())
    {
        mCtr.add(mValue);
    }
}

bool CtrThread::keeprunning()
{
    QMutexLocker ml(&mMutex);
    return ! mStop;
}

test.cpp

#include <QtCore>
#include <QTest>
#include "objs.h"

int main(int argc, char** argv)
{
    QCoreApplication app(argc, argv);

    qDebug() << "Initalising";

    Counter ctr(0);
    CtrThread thread_a(ctr, +1);
    CtrThread thread_b(ctr, -1);

    qDebug() << "Starting Threads";

    thread_a.start();
    thread_b.start();

    for (int i = 0; i != 15; ++i)
    {
        qDebug() << "Counter value" << ctr.add(0);
        QTest::qSleep(1000);
    }

    qDebug() << "Stopping Threads";

    thread_a.stop();
    thread_b.stop();
    thread_a.wait();
    thread_b.wait();

    qDebug() << "Finished";
    return 0;
}

test.pro

QT=core testlib
HEADERS=objs.h
SOURCES=test.cpp objs.cpp

コンパイルして実行すると、値が出力され、出力例が表示されます。

Initalising
Starting Threads
Counter value 0
Counter value 11057
Counter value 28697
Counter value 50170
Counter value 60678
Counter value 73773
Counter value 84898
Counter value 96441
Counter value 118795
Counter value 135293
Counter value 146107
Counter value 158688
Counter value 169886
Counter value 201203
Counter value 212983
Stopping Threads
Finished
29
Silas Parker

OK、実際のプロジェクトの優れたソリューションを提供してくれた@skyhisiに特に感謝します。

私は@skyhisiの投稿と、QMutexと共有変数に関する他の記事を読みました。教育目的で、変数を共有するためのQMutexの使用法のシンプルで明確なサンプルを実装しました(この場合はcounter)。

_class thread_a : public QThread
{
    Q_OBJECT
public:
    thread_a(QMutex*, int*);
    void shutdown();

private:
    QMutex* mutex;
    int* counter;
    bool isShutdownRequested;

protected:
    void run();
};
_

および_thread_a.cpp_ファイル内:

_thread_a::thread_a(QMutex * m, int* i)
{
    counter=i;
    isShutdownRequested=false;
    mutex=m;
}

void thread_a::run()
{
    isShutdownRequested=false;
    forever{
        //lock mutex for changing in shared variable
        mutex->lock();
        *counter=*counter+1;
        mutex->unlock();

        if(isShutdownRequested)
            break;
    }
}

void thread_a::shutdown()
{
    isShutdownRequested=true;
}
_

myMainWindow::RunThreads(bool bl)スロット:

_int cnt=0;
QMutex mu;
thread* a=new thread_a(&mu, &cnt);
thread* b=new thread_b(&mu, &cnt);
a.start();
b.start();
_

myMainWindow::~myMainWindow() deconstructorで:

_a->shutdown();
b->shutdown();
_

もう一度ありがとう@skyhisi

3
S.M.Mousavi

QMutexの「ヘルプ」を参照する簡単な例を作成します。この例では、2つのスレッドが(モニターとして)同じ数を変更します。また、S.M.Mousaviのコードも参照しています。ここにコードがあります:

//main.cpp

#include <QCoreApplication>
#include "method.h"

int aNum=0;
QMutex aMutex;

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);

    int *p_no= &aNum;
    QMutex *p_Mu = &aMutex;

    method mThread1(p_Mu, p_no);
    method mThread2(p_Mu, p_no);

    mThread1.name = "one";
    mThread2.name = "two";

    mThread1.start();
    mThread2.start();

   return a.exec();
}

// method.h

#ifndef METHOD_H
#define METHOD_H

#include <QDebug>
#include <QThread>
#include <QtCore>
#include <QString>
#include <QMutex>


class method: public QThread
{
public:
    method(QMutex *mu, int *nu);
    void run();
    void method1();
    void method2();
    QString name;

private:
    int *number;
    QMutex *myMutex;
};

#endif // METHOD_H

//method.cpp #include "method.h"

method::method(QMutex *mu, int *nu)
{
    myMutex = mu;
    number = nu;
}


void method:: run()
{
    for (int i = 0; i<100; i++)
    {
        if(this->name == "one" )
        {
            this->method1();
        }
        else
        {
            this->method2();
        }
    }
}

void method::method1()
{
    myMutex->lock();
    *number += 1;
    qDebug()<<*number<<"---"<<this->name;
    myMutex->unlock();
}

void method ::method2()
{
    myMutex->lock();
    *number -= 1;
    qDebug()<<*number<<"---"<<this->name;
    myMutex->unlock();
}
1
maroon912