使用Qt以不同方法进行线程化

时间:2011-05-08 16:36:14

标签: c++ multithreading qt

我有一个有几个方法的类,我想在不同的线程中调用每个方法。

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

这是可能的,还是应该继承在run()方法中实现功能的不同类? (这将导致几个派生类)

谢谢。

3 个答案:

答案 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()方法? (这将导致几个派生类)

这取决于您是希望这些方法同时执行(在线程池上,还是在专用线程上),还是序列化。

  1. 如果序列化:

    使用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;
    };
    
  2. 如果并发:

    1. 如果在线程池上:

      使用QThreadPool / QRunnable。与上述代码相同,但将QThread / QMutex / QQueue / QWaitCondition四重奏替换为QThreadPool,将Command替换为QRunnableBase::enqueue()QThreadPool::start()

      使用线程池时,请记住shouldn't put (potentially) blocking operations on QThreadPool::globalInstance()(如果操作(如我的示例中)(可能)阻塞,则创建本地QThreadPool

    2. 如果使用专用线程:

      不要从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();
...