Ответ 1
Вместо того, чтобы иметь данные в потоке, перемещайте данные за пределы потока, защитите его, а затем получите доступ к нему из обоих потоков.
Ниже приведен эскиз того, что вы могли бы сделать:
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
часто называют Monitor, из Википедии (выделено мной):
При параллельном программировании монитор - это объект или модуль, предназначенные для безопасного использования более чем одним потоком. Определяющей характеристикой монитора является то, что его методы выполняются с обоюдным исключением. То есть в каждый момент времени не более одного потока может выполняться любой из его методов. Это взаимное исключение значительно упрощает рассуждения о реализации мониторов по сравнению с рассуждением о параллельном коде, который обновляет структуру данных.
В этом конкретном случае более эффективная конструкция будет 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