Reputation: 1452
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
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
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
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.
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;
};
If concurrent:
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).
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 QThread
s 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