Important: Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

QtConcurrent single-threaded sequence processing



  • Hi

    I'm working on a QWidget application. I have a relatively long running task that I want to run in a background thread to avoid blocking the GUI thread. The task consist of sending a sequence of commands to a camera. This can be modeled as calling a function with a sequence of commands.

    I have looked through the documentation and came across QtConcurrent which filter, filter-reduce, map, map-reduce etc. Together with a QFutureWatcher this provides a lot of functionality out-of-the-box which I'd like to use (showing progress, cancellation, notification of the GUI thread etc.). I can pass my sequence of commands together with a function to send a command to the camera to some QtConcurrent function.

    My issue is that the order of commands is important. Therefore, I do not want multiple threads processing the command sequence in an arbitrary order but only a single thread processing the command sequence in a defined order.

    Can this somehow be achieved with the built-in higher level API's or do I have to resort using QtConcurrent::run() and implementing stuff like progress reporting etc. myself?



  • Hello,

    I think this will be hard to achieve using QtConcurrent. Because you can only gives a pointer to a function for be run in a different thread, so you cannot connect some signals to indicate progress.

    But you could do it by calling QtConcurrent::run() for each of the command sent to the camera, so when a command is finished, you can send the next one, and then provide the progress quite easily.

    Another ways are to use QThread with the worker object approach, or QThreadPool and QRunnable. Both will require some extra work, but you will also have more possibilities.

    You can inherit from both QRunnable and QObject to provide signal monitoring signals, here is a small example:

    class HelloWorldTask : public QObject, public QRunnable
    {
        Q_OBJECT
        
        volatile bool m_abort;
        QReadWriteLock m_lock;
    public:
        void run()
        {
            emit started();
            qDebug() << "Hello world from thread" << QThread::currentThread();
            emit progressMaxChanged(10);
            emit progressValueChanged(0);        
            for(int i = 0; i < 10; i++){                  
                if(isAborted())
                    break;
                QThread::msleep(500);
                emit progressValueChanged(i+1);        
            }
            emit finished();
        }
    public slots:
        void abort()    
        {        
          QWriteLocker locker(&m_lock)
          m_abort = true;
          lock.unlock();
        }
    protected:
        bool isAborted(){
            QReadLocker locker(&m_lock);
            return m_abort;
        }
    signals:
        void started();
        void progressValueChanged(int progress);
        void progressMaxChanged(int max);
        void finished();
    };
    
    HelloWorldTask *hello = new HelloWorldTask();
    connect(hello, &HelloWorldTask::started, this, &MyClass::onStarted);
    connect(hello, &HelloWorldTask::progressValueChanged, this, &MyClass::onProgressValueChanged);
    connect(hello, &HelloWorldTask::progressMaxChanged, this, &MyClass::onProgressMaxChanged);
    connect(hello, &HelloWorldTask::finished, this, &MyClass::onFinished);
    connect(this, &MyClass::abort, hello, &HelloWorldTask::abort);
    // QThreadPool takes ownership and deletes 'hello' automatically
    QThreadPool::globalInstance()->start(hello);
    

    Based on this you can reproduce almost the same behavior than QtConcurrent and QFuture and QFutureWatcher.


  • Lifetime Qt Champion

    Hi,

    Doing sequential work like that with Qt Concurrent is a bit of an oxymoron. From what you describe, it seems like a state machine would be more fitting to model what you want to do with your camera.

    See the The State Machine Framework chapter in Qt's documentation.


Log in to reply