Проблема с доступом к переменной из разных потоков с помощью QAtomicInt

Всем Доброго здравия!

есть класс Device у него есть счетчик m_time, значение которого может асинхронно относительно основного потока изменяться по таймеру или пользователем. Какие мне нужно применить механизмы синхронизации, что избежать одновременного обращения к переменной m_time? вот моя наивная реализация кода:

#include <QtWidgets>

class Device : public QObject {
Q_OBJECT
private:
    uint    m_time;
    QTimer* m_ptimer;

    bool m_isChanging;
    bool m_isClocking;

public:
    Device(QObject* pobj = 0) : QObject(pobj)
                                , m_time(10)
      , m_isChanging(false)
      , m_isClocking(false)
    {
        m_ptimer = new QTimer(this);

        connect(m_ptimer, SIGNAL(timeout()), SLOT(setNextValue()));

        m_ptimer->start(1000);
    }

signals:
    void valueChanged(int);
    void change      (   );

public slots:

     void slotChangeValue(int value)
    {
        if (!m_isClocking) // если не инкремент-тится то изменяем значение
        {
           m_isChanging = true;
           m_time = value;
           m_isChanging = false;
        }
        else
        {
            // счетчик занят, пробуйте изменить значение позже
        }
    }

private slots:
    void setNextValue()
    {
        if (!m_isChanging) // если не значение не занято редактированием, то инкремент-им
        {
           m_isClocking = true;
           ++m_time;
           m_isClocking = false;
        }
        else
        {
            // пропуск одно "тика" не критичен
        }
    }
};

QRandomGenerator *rg = QRandomGenerator::global();

// ----------------------------------------------------------------------
int main(int argc, char** argv)
{
    QApplication app(argc, argv);
    Device     worker;

    // имитируем рамдомное редактирование счетчика пользователем
    while (true)
    {
       int change = rg->bounded(1, 100);
       if (change  % 2 == 0)
       {
           worker.slotChangeValue(change);
       }
    }

 
    int nResult = app.exec();

    return nResult;
}

Хотелось бы обойтись без блокирующей синхронизации типа QMutexLocker, например сделать переменную атомарной QAtomicInt m_time; но нужны ли тогда флаги m_isChanging, m_isClocking?


Ответы (1 шт):

Автор решения: kitworker

Учитывая замечания, так вроде работает:

#include <QtWidgets>
#include <QDebug>

// ======================================================================

QRandomGenerator *rg = QRandomGenerator::global();

class UserTest : public QThread {
Q_OBJECT

public:
    void run()
    {
        while (!isInterruptionRequested())
        {
            int change = rg->bounded(1, 100);
            emit setValue(change);
            msleep(change);
        }
    }

signals:
    void setValue(int);
};


class Device : public QObject {
Q_OBJECT
private:
    QAtomicInt    m_time;
    QTimer* m_ptimer;

public:
    Device(QObject* pobj = 0) : QObject(pobj)
                                , m_time(10)
    {
        m_ptimer = new QTimer(this);

        connect(m_ptimer, SIGNAL(timeout()), SLOT(setNextValue()));

        m_ptimer->start(100);
    }

signals:
    void valueChanged(int);
    void sendValue(int);

public slots:

     void slotChangeValue(int value)
    {
         Q_ASSERT(m_time < 200);
         m_time.store(value);
         qDebug() << m_time;
        //else  счетчик занят, пробуйте изменить значение позже
    }

private slots:
    void setNextValue()
    {
         Q_ASSERT(m_time < 200);
         m_time.fetchAndAddOrdered(1);
         qDebug() << m_time;
         sendValue(m_time);
         // пропуск одно "тика" не критичен
    }
};


// ----------------------------------------------------------------------
int main(int argc, char** argv)
{
    QApplication app(argc, argv);
    QProgressBar prb;
    Device     device;
    UserTest   test;

    QObject::connect(&test, SIGNAL(setValue(int)),
                     &device,    SLOT(slotChangeValue(int))
                    );

    QObject::connect(&device, SIGNAL(sendValue(int)),
                     &prb,    SLOT(setValue(int))
                    );

    prb.show();

    test.start();

    return app.exec();
}

#include "main.moc"
→ Ссылка