我有一个有几个方法的类,我想在不同的线程中调用每个方法。
class Base : public QThread
{
copy();
move();
remove();
etc();
....
....
run();
}
这是可能的,还是应该继承在run()方法中实现功能的不同类? (这将导致几个派生类)
谢谢。
答案 0 :(得分:3)
实际上,QThread :: run()只是一个启动新线程的方法。只需从不同的线程调用方法,它们就会在那里执行。
从设计开始:线程间通信通常围绕消息传递模型构建,Qt的排队连接模式使其易于实现:您只需将方法设置为插槽,并在run()中启动QMessageLoop通过exec()方法:
class Base : public QThread
{
public slots:
void copy();
void move();
void remove();
void etc();
...
protected:
void run()
{
exec();
}
}
现在您可以通过信号调用您的方法,Base的每个实例都将在自己的线程中执行它们。
答案 1 :(得分:2)
这是可能的还是我应该继承实现该功能的不同类 他们的
run()
方法? (这将导致几个派生类)
这取决于您是希望这些方法同时执行(在线程池上,还是在专用线程上),还是序列化。
如果序列化:
使用Actor model。另一个词是Active Object。 C ++和Qt都不支持开箱即用的Actors,所以你必须把它作为模式。这应该让你开始:
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;
};
如果并发:
如果在线程池上:
使用QThreadPool
/ QRunnable
。与上述代码相同,但将QThread
/ QMutex
/ QQueue
/ QWaitCondition
四重奏替换为QThreadPool
,将Command
替换为QRunnable
,Base::enqueue()
与QThreadPool::start()
。
使用线程池时,请记住shouldn't put (potentially) blocking operations on QThreadPool::globalInstance()
(如果操作(如我的示例中)(可能)阻塞,则创建本地QThreadPool
。
如果使用专用线程:
不要从Base
继承QThread
,而是让每个Base
函数创建并启动自己的MoveCommand
等,从QThread
派生,然后重新实现run()
。这些finished()
的{{1}}信号应与其QThread
位置相关联,以确保清理。
虽然这可能与您最初想要的最接近,但您实际上并不想为每个文件操作使用一个线程(假设它们是文件操作),因为它们的创建成本相对较高。即使这些东西是CPU限制的,使用专用线程也很容易导致CPU过度提交,而且一切都比以前慢。
答案 2 :(得分:1)
至于葡萄藤的答案。您不仅可以通过信号发出这些插槽。您还可以使用以下代码:
Base *pBase = new Base;
QMetaObject::invokeMethod(pBase, "copy");
如果copy
方法应该接受参数,请使用Q_ARG
宏来传递它们。助理有一些很好的例子(搜索invokeMethod()
)。
还有一件事,如果您不想将这些方法声明为插槽,但仍然需要QMetaObject::invokeMethod()
可以调用它,您可以在Q_INVOKABLE
宏之前添加它。
...
public:
Q_INVOKABLE void copy();
...