Arsham
Arsham

Reputation: 1452

Threading in different methods with Qt

I have a class which has several methods and I want to call each of those methods in a different thread.

class Base : public QThread
{
    copy();
    move();
    remove();
    etc();
    ....    
    ....
    run();
}

Is this possible or should I inherit different classes which implement the functionality in their run() method? ( which would lead to several derived classes )

Thank you.

Upvotes: 1

Views: 3359

Answers (3)

casper
casper

Reputation: 11

As for addition to vines's answer. You could issue those slots not only via signals. You could also use the following code:

Base *pBase = new Base;
QMetaObject::invokeMethod(pBase, "copy");

If the copy method should accept arguments, use Q_ARG macros to pass them. The assistant has some good examples (search for invokeMethod()). One more thing, if you don't want do declare those methods as slots, but still need it to be invokable by the QMetaObject::invokeMethod(), you could prepend it with the Q_INVOKABLE macros.

...
public:
    Q_INVOKABLE void copy();
...

Upvotes: 1

vines
vines

Reputation: 5225

In fact, QThread::run() is only a method to start a new thread. Just call the methods from different threads, and they will be executed there.

As of design: inter-thread communication is frequently built around the message-passing model, and Qt's queued connection mode makes it simple to implement: you just make your methods to be slots, and in run() you just start a QMessageLoop via the exec() method:

class Base : public QThread
{
public slots:
    void copy();
    void move();
    void remove();
    void etc();

...

protected:
    void run()
    {
        exec();
    }
}

Now you can call your methods via signals, and each instance of Base will execute them in its own thread.

Upvotes: 3

Marc Mutz - mmutz
Marc Mutz - mmutz

Reputation: 25293

Is this possible or should I inherit different classes which implement the functionality in their run() method? ( which would lead to several derived classes )

That depends on whether you want those methods to execute concurrently (on a thread pool, or on dedicated threads), or serialized.

  1. If serialized:

    Use the Actor model. Another word for that is Active Object. Neither C++ nor Qt support Actors out of the box, so you have to take it as a pattern. This should get you started:

    class Base : public QObject {
        Q_OBJECT
    public:
        explicit Base( QObject * parent=0 )
            : QObject( parent ),
              thread(),
              queue(),
              mutex(),
              queueNotEmpty()
        {
            thead.start();
        }
        ~Base() {
            // shut down thread:
            enqueue( 0 ); // end marker
            thread.wait(); // join with worker thread
        }
    public Q_SLOTS:
        void copy( const QString & from, const QString & to ) {
            enqueue( new CopyCommand( from, to ) );
        }
        void move( const QString & from, const QString & to ) {
            enqueue( new MoveCommand( from, to ) );
        }
    
    private:
        struct Command {
            virtual ~Command() {}
            virtual void exec() = 0;
        }
        class CopyCommand : public Command {
            const QString from, to;
            CopyCommand( const QString & from, const QString & to )
                : Command(), from( from ), to( to ) {} 
            void exec() { QFile::copy( from, to ); }
        };
        class MoveCommand : public Command {
            // ...
        };
        // ...
    private:
        void enqueue( Command * cmd ) {
            const QMutexLocker locker( &mutex );
            queue.enqueue( cmd );
            queueNotEmpty.wakeOne();
        }
    
        /* reimpl */ void run() {
            while ( true ) {
                QMutexLocker locker( &mutex );
                while ( queue.isEmpty() )
                    queueNotEmpty.wait( &mutex );
                Command * cmd = queue.dequeue();
                locker.unlock():
                if ( !cmd ) return; // end marker
                cmd->exec();
                delete cmd;
            }
        }
    private:
        QThread thread;
        QQueue<Command*> queue;
        QMutex mutex; // protects 'queue'
        QWaitCondition queueNotEmpty;
    };
    
  2. If concurrent:

    1. If on a thread pool:

      Use QThreadPool/QRunnable. Same code as above, but replace the QThread/QMutex/QQueue/QWaitCondition quartet with QThreadPool, Command with QRunnable, and Base::enqueue() with QThreadPool::start().

      When using a thread pool, remember that you shouldn't put (potentially) blocking operations on QThreadPool::globalInstance() (so create a local QThreadPool if the operations, like in my example, are (potentially) blocking).

    2. If using dedicated threads:

      Don't inherit Base from QThread, but make every Base function create and start its own MoveCommand etc, deriving from QThread, and reimplementing run(). The finished() signal of these QThreads should be connected to their deleteLater() slots as a way to ensure cleanup.

      While this is probably closest to what you wanted originally, you really don't want to use one thread for each file operation (assuming they're file operations), since they're relatively expensive to create. Even if the stuff is CPU-bound, using dedicated threads can easily result in over-committing the CPUs, and everything runs slower than before.

Upvotes: 2

Related Questions