Invece di avere i dati all'interno di un thread, spostare i dati all'esterno del thread, proteggerlo e quindi accedervi da entrambi i thread.
Quello che segue è uno schizzo, di che cosa si potrebbe fare:
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);
/* ... */
};
Il costrutto di Counter
è spesso definito come un Monitor, da Wikipedia (sottolineatura mia):
In concomitante programmazione, un monitor è un oggetto o modulo destinato ad essere utilizzato in modo sicuro da più di un thread. La caratteristica che definisce un monitor è che i suoi metodi sono eseguiti con mutua esclusione. Cioè, in ogni momento, al massimo un thread può eseguire uno qualsiasi dei suoi metodi. Questa esclusione reciproca semplifica enormemente il ragionamento sull'implementazione dei monitor rispetto al ragionamento sul codice parallelo che aggiorna una struttura dati.
In questo caso specifico, un costrutto più efficiente sarebbe QAtomicInt
. Questo guadagna atomicità dall'uso di speciali istruzioni della CPU. Questa è una classe di basso livello che potrebbe essere utilizzata per implementare altri costrutti di threading.
Edit - Esempio completo
Utilizzando correttamente le discussioni con lo stato condiviso non è banale. Si consiglia di prendere in considerazione l'utilizzo di segnali/slot Qt con connessioni in coda o altri sistemi basati su messaggi.
In alternativa, altri linguaggi di programmazione come Ada supportano Thread e Monitor (oggetti protetti) come costrutti nativi.
Ecco un esempio operativo completo. Questo è solo un codice di esempio, non utilizzare QTest::qSleep
in codice reale.
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
Compilare ed eseguire, si vedrà il valore in fase di stampa, esempio di output:
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
** In questo caso non è necessario un mutex. ** Sembra che ci siano 2 classi separate per ogni thread, come suggerito da 'class thread_a' dichiarazione. Ciò significa che quando entrambe le classi sono istanziate, ciascuna avrà la propria copia di 'counter int;' e la diminuzione del 'counter' di un oggetto non influenzerà il' counter' dell'altra. – karlphillip
Grazie a @karlphillip, hai ragione. Ma se voglio condividere 'counter' con' thread_a' e 'thread_b' che cosa suggerisci (usando' QMutex' solo per scopi didattici) –
possibile duplicato di [Cercando di avere problemi con QThread] (http://stackoverflow.com)/questions/6084110/try-to-get-problems-with-qthread) – karlphillip