Приостановить QThread - PullRequest
       1

Приостановить QThread

1 голос
/ 30 апреля 2011

UpdateThread is-a QThread Это устанавливает QTimer в UpdateThread::run(), который вызывает слот UpdateThread::tick() каждые t мс.Теперь, основываясь на каком-то условии, мне нужно Pause Поток, а после какого-то времени, основываясь на другом условии, мне нужно его разбудить.

  • Хорошо, что я делаю QTimer?или я должен переместить код tick на run и вызвать QThread::start() каждые t мс?
  • Как я могу приостановить и условно разбудить потоки
  • Или я долженпросто stop() QTimer и start() это последнее?

1 Ответ

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

Прежде всего, вы не должны определять слоты в своем подклассе QThread и вызывать их изнутри run() - слоты будут выполняться (выполняя перекрестный вызов слотов) в контексте потока, который владеет вашим UpdateThread экземпляром (тем же, который его создал, если вы не вызвали moveToThread() для него), а не в контексте потока, представленного UpdateThread. Запомните эту мнемонику:

In run(), QThread::thread() != this

Вместо этого определите слоты в подклассе QObject, который вы создаете внутри run().

Хорошо, с этим из пути, давайте посмотрим на таймер. Документация QTimer содержит следующее:

В многопоточных приложениях вы можете использовать QTimer в любом потоке, в котором есть цикл обработки событий. Чтобы запустить цикл обработки событий из потока без графического интерфейса, используйте QThread::exec(). Qt использует таймер аффинность потока , чтобы определить, какой поток будет излучать сигнал timeout(). Из-за этого вы должны запускать и останавливать таймер в его потоке; это невозможно запустить таймер из другого потока.

(выделено мной) Обратите особое внимание на последнее предложение.

Решение состоит в том, чтобы сделать вызов между потоками QTimer::start() и QTimer::stop(). Возможно, вы знаете о сквозных соединениях сигнал / слот. При этом используется тот же базовый механизм, который представлен в QMetaObject::invokeMethod():

class UpdateThread : public QThread {
    Q_OBJECT
private:
    QObject * m_timer; // just a QObject* so we're not tempted
                       // to call functions on it
    QMutext m_mutex; // protects 'm_timer'
public:
    explicit UpdateThread( QObject * parent=0 )
        : QThread( parent ), m_timer( 0 ) {}
    // ...
private:
    /* reimpl */ void run() {
        QTimer timer;
        // ...'timer' setup code goes here...
        {
            const QMutexLocker locker( &m_mutex );
            m_timer = &timer; // publish 'timer' through 'm_timer'
        }
        // main code of run()
        exec(); // start event loop so we get timer's timeout()s
                // and we can receive cross-thread method calls
        {
            const QMutexLocker locker( &m_mutex );
            m_timer = 0; // un-publish before we delete `timer`
        }
    }
public Q_SLOTS:
    void startTimer() {
        const QMutexLocker locker( &m_mutex );
        if ( !m_timer ) return;
        // perform cross-thread method call:
        QMetaObject::invokeMethod( m_timer, "start", Qt::QueuedConnection );
    }
    void stopTimer() {
        const QMutexLocker locker( &m_mutex );
        if ( !m_timer ) return;
        // perform cross-thread method call:
        QMetaObject::invokeMethod( m_timer, "stop", Qt::QueuedConnection );
    }
};

Теперь вы можете запускать / останавливать таймер из потока графического интерфейса. Но вы также спрашивали об альтернативах.

  1. Перемещение tick() кода в run(), вызов UpdateThread::start() каждые t миллисекунд.

    Это неоптимально, поскольку оно будет создавать и уничтожать потоки каждые t мс. Создание потока - все еще дорогая операция. Кроме того, если к следующему звонку start() не будет выполнено UpdateThread::run(), вы потеряете отметки таймера.

  2. UpdateThread, как указано выше.

    Это не так уж плохо, но это не идиоматическая многопоточность, я бы сказал. Это хорошее решение, если таймер срабатывает так часто, что это само по себе может как-то замедлить поток GUI, хотя вы можете потерять тики таймера и в этом случае.

  3. QThreadPool

    Мой любимый. Переместите код, который выполняет tick(), в реализацию QRunnable::run() и ставьте новый исполняемый файл в очередь потоков при каждом срабатывании таймера. В этом случае таймер наиболее естественно будет жить в потоке графического интерфейса, избегая необходимости перекрестных вызовов методов, как описано выше. Если сам поток GUI не перегружен, вы не пропустите ни одного такта таймера. Вы также получаете бесплатное масштабирование до количества ядер в системе (если вы этого не хотите, не используйте QThreadPool::globalInstance(), но создайте свой собственный экземпляр и вызовите setMaxThreadCount(1)).

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...