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