Потоки в разных методах с Qt - PullRequest
1 голос
/ 08 мая 2011

У меня есть класс с несколькими методами, и я хочу вызвать каждый из этих методов в отдельном потоке.

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

Возможно ли это, или я должен наследовать разные классы, которые реализуют функциональность в их методе run ()? (что привело бы к нескольким производным классам)

Спасибо.

Ответы [ 3 ]

3 голосов
/ 08 мая 2011

На самом деле 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 будетвыполнить их в своем собственном потоке.

2 голосов
/ 09 мая 2011

Возможно ли это, или я должен наследовать разные классы, которые реализуют функциональность в их run() методе?(что приведет к нескольким производным классам)

Это зависит от того, хотите ли вы, чтобы эти методы выполнялись одновременно (в пуле потоков или в выделенных потоках) или сериализовались.

  1. При сериализации:

    Использовать модель актера .Другое слово для этого - Активный объект .Ни 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 на QRunnable и Base::enqueue() на QThreadPool::start().

      При использовании пула потоков помните, что не следует (потенциально) ставить блокирующие операции на QThreadPool::globalInstance() (поэтому создайте локальный QThreadPool, если операции, как в моем примере, (потенциально)) блокировка).

    2. При использовании выделенных потоков:

      Не наследовать Base от QThread, но заставить каждую функцию Base создавать и запускать еесобственный MoveCommand и т. д., происходящий от QThread и переопределенный run().Сигнал finished() этих QThread должен быть подключен к их deleteLater() слотам в качестве способа обеспечения очистки.

      Хотя это, вероятно, ближе всего к тому, что вы изначально хотели, вы действительно не хотитеиспользовать один поток для каждой файловой операции (при условии, что они являются файловыми операциями), поскольку их создание относительно дорого.Даже если содержимое связано с процессором, использование выделенных потоков может легко привести к перегрузке процессоров, и все работает медленнее, чем раньше.

1 голос
/ 15 июля 2011

Что касается дополнения к ответу лозы. Вы можете выдавать эти слоты не только через сигналы. Вы также можете использовать следующий код:

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

Если метод copy должен принимать аргументы, используйте макросы Q_ARG для их передачи. У помощника есть несколько хороших примеров (поиск по invokeMethod()). Еще одна вещь, если вы не хотите объявлять эти методы как слоты, но по-прежнему хотите, чтобы они вызывались QMetaObject::invokeMethod(), вы можете добавить его к макросам Q_INVOKABLE.

...
public:
    Q_INVOKABLE void copy();
...
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...