Вопрос

Я новичок в Qt и ищу многопоточность в Qt.
Как я узнал в Qt-документы, я определил два класса для двух потоков:

#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++;
}

Второй класс потока такой же, но с counter-- в run() метод.
Затем я запускаю эти два потока из main.ccp.

Теперь мой вопрос:
Как я могу поделиться counter в thread_a и thread_b с использованием QMutex?

Это было полезно?

Решение

Вместо того, чтобы хранить данные внутри потока, переместите данные за пределы потока, защитите их, а затем получите к ним доступ из обоих потоков.

Ниже приводится набросок того, что вы могли бы сделать:

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 часто называют Монитор, из Википедии (выделено мной):

В параллельном программировании монитор — это объект или модуль, предназначенный для безопасного использования более чем одним потоком.Определяющей характеристикой монитора является то, что его методы выполняются на основе взаимного исключения.То есть в каждый момент времени не более одного потока может выполнять любой из своих методов.Этот взаимное исключение значительно упрощает рассуждения о реализации мониторов по сравнению с рассуждениями о параллельном коде, обновляющем структуру данных..

В данном конкретном случае более эффективной конструкцией будет QAtomicInt.Это приобретает атомарность за счет использования специальных инструкций ЦП.Это класс низкого уровня, который можно использовать для реализации других конструкций потоков.


Изменить — полный пример

Правильное использование потоков с общим состоянием — нетривиальная задача.Возможно, вы захотите рассмотреть возможность использования сигналов/слотов 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

Другие советы

Хорошо, особая благодарность @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() деконструктор:

a->shutdown();
b->shutdown();

Еще раз спасибо @skyhisi

Я пишу простой пример, на который ссылается на «справку» Qmutex, в котором два потока изменяют одно и то же число (как монитор).Это также код 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 "метод.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();
}
.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top