c++ - ¿Cómo usar QMutex?
multithreading qt (3)
Soy novato en Qt y estoy buscando multihilo en Qt.
Como aprendí en Documentos Qt , definí dos clases para dos hilos:
#include <QThread>
#include <QMutex>
class thread_a : public QThread
{
Q_OBJECT
public:
explicit thread_a(QObject *parent = 0);
int counter;
protected:
void run();
};
Y en el archivo CPP:
#include "thread_a.h"
thread_a::thread_a(QObject *parent) :
QThread(parent)
{
counter=0;
}
void thread_a::run()
{
counter++;
}
La segunda clase de subprocesos es la misma, pero con el método counter--
en run()
Luego ejecuto estos dos hilos de main.ccp
.
Ahora mi pregunta:
¿Cómo puedo compartir el counter
en thread_a
y thread_b
usando QMutex
?
Bien, gracias especiales a @skyhisi por la gran solución para un proyecto real.
Leí la publicación @skyhisi y más artículos sobre QMutex y cómo compartir variables. Para fines educativos, implementé una muestra simple / clara del uso de QMutex para compartir la variable (este counter
casos).
class thread_a : public QThread
{
Q_OBJECT
public:
thread_a(QMutex*, int*);
void shutdown();
private:
QMutex* mutex;
int* counter;
bool isShutdownRequested;
protected:
void run();
};
y en el archivo 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;
}
En la 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();
En myMainWindow::~myMainWindow()
deconstructor:
a->shutdown();
b->shutdown();
Gracias de nuevo @skyhisi
En lugar de tener los datos dentro de un hilo, mueva los datos fuera del hilo, protéjalos y luego acceda a ellos desde ambos hilos.
El siguiente es un bosquejo, de lo que podrías hacer:
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);
/* ... */
};
El constructo de Counter
menudo se conoce como un Monitor , de Wikipedia (el énfasis es mío):
En la programación concurrente, un monitor es un objeto o módulo destinado a ser usado de manera segura por más de un hilo. La característica definitoria de un monitor es que sus métodos se ejecutan con exclusión mutua. Es decir, en cada momento, como máximo, un hilo puede estar ejecutando cualquiera de sus métodos. Esta exclusión mutua simplifica en gran medida el razonamiento sobre la implementación de monitores en comparación con el razonamiento sobre el código paralelo que actualiza una estructura de datos .
En este caso específico, una construcción más eficiente sería QAtomicInt
. Esto gana atomicidad por su uso de instrucciones especiales de CPU. Esta es una clase de bajo nivel que podría usarse para implementar otras construcciones de subprocesos.
Editar - Ejemplo completo
Usar hilos con el estado compartido correctamente no es trivial. Es posible que desee considerar el uso de señales / ranuras Qt con conexiones en cola u otros sistemas basados en mensajes.
Alternativamente, otros lenguajes de programación como Ada admiten subprocesos y monitores (objetos protegidos) como construcciones nativas.
Aquí hay un ejemplo completo de trabajo. Este es solo un código de muestra, no use QTest::qSleep
en código real.
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
Compilar y ejecutar, verá el valor que se está imprimiendo, salida de muestra:
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
Escribo un ejemplo simple que hace referencia a la "ayuda" de QMutex, en la que dos hilos cambian un mismo número (como un Monitor). También refiera el código de SMMousavi. Aquí está el código:
//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();
}
// metodo.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();
}