Подтвердить что ты не робот

Запуск QTimer в QThread

Я пытаюсь запустить QTimer в определенном потоке. Однако таймер не работает, и ничего не распечатывается. Это как-то связано с таймером, слотом или потоком?

main.cpp

    #include "MyThread.h"
    #include <iostream>
    using namespace std;

    int main(int argc, char *argv[]) {
        MyThread t;
        t.start();
        while(1);
    }

MyThread.h

    #ifndef MYTHREAD_H
    #define MYTHREAD_H

    #include <QTimer>
    #include <QThread>
    #include <iostream>

    class MyThread : public QThread {
        Q_OBJECT
    public:
        MyThread();
    public slots:
        void doIt();
    protected:
        void run();
    };

    #endif  /* MYTHREAD_H */

MyThread.cpp

    #include "MyThread.h"

    using namespace std;

    MyThread::MyThread() {
        moveToThread(this);
    }

    void MyThread::run() {
        QTimer* timer = new QTimer(this);
        timer->setInterval(1);
        timer->connect(timer, SIGNAL(timeout()), this, SLOT(doIt()));
        timer->start();
    }

    void MyThread::doIt(){
        cout << "it works";
    }
4b9b3361

Ответ 1

Как я уже говорил (дополнительная информация в ссылке), вы делаете это неправильно:

  • Вы смешиваете объект, содержащий данные потока с другим объектом (ответственным за doIt()). Они должны быть разделены.
  • В вашем случае нет подкласса QThread. Хуже того, вы переопределяете метод run без какого-либо рассмотрения того, что он делает.

Эта часть кода должна быть достаточно

QThread* somethread = new QThread(this);
QTimer* timer = new QTimer(0); //parent must be null
timer->setInterval(1);
timer->moveToThread(somethread);
//connect what you want
somethread->start();

Теперь (версия Qt >= 4.7) по умолчанию QThread запускает цикл событий в своем методе run(). Чтобы запустить внутри потока, вам просто нужно переместить объект. Прочитайте документ...

Ответ 2

m_thread = new QThread(this);
QTimer* timer = new QTimer(0); // _not_ this!
timer->setInterval(1);
timer->moveToThread(m_thread);
// Use a direct connection to whoever does the work in order
// to make sure that doIt() is called from m_thread.
worker->connect(timer, SIGNAL(timeout()), SLOT(doIt()), Qt::DirectConnection);
// Make sure the timer gets started from m_thread.
timer->connect(m_thread, SIGNAL(started()), SLOT(start()));
m_thread->start();

Ответ 3

A QTimer работает только в потоке, который имеет цикл событий.

http://qt-project.org/doc/qt-4.8/QTimer.html

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

Ответ 4

Вы можете использовать излучающий сигнал и запустить таймер внутри функции испускаемого слота

main.cpp

#include "MyThread.h"
#include <iostream>
using namespace std;

int main(int argc, char *argv[]) {
    MyThread t;
    t.start();
    while(1);
}

MyThread.h

#ifndef MYTHREAD_H
#define MYTHREAD_H

#include <QTimer>
#include <QThread>
#include <iostream>

class MyThread : public QThread {
    Q_OBJECT
public:
    MyThread();
    QTimer *mTimer;
signals:
   start_timer();
public slots:
    void doIt();
    void slot_timer_start();
protected:
    void run();
};

#endif  /* MYTHREAD_H */

MyThread.cpp

#include "MyThread.h"

using namespace std;

MyThread::MyThread() {
    mTimer = new QTimer(this);
    connect(this,SIGNAL(start_timer()),this, SLOT(slot_timer_start()));
    connect(mTimer,SIGNAL(timeout()),this,SLOT(doIt()));

}

void MyThread::run() {
    emit(start_timer());
    exec();
}

void MyThread::doIt(){
    cout << "it works";
}
void MyThread::slot_timer_start(){
    mTimer->start(1000);
}

Ответ 5

Вам нужен цикл событий, чтобы иметь таймеры. Вот как я решил ту же проблему с моим кодом:

MyThread::MyThread() {
}

void MyThread::run() {
    QTimer* timer = new QTimer(this);
    timer->setInterval(1);
    timer->connect(timer, SIGNAL(timeout()), this, SLOT(doIt()));
    timer->start();

    /* Here: */
    exec();             // Starts Qt event loop and stays there
   // Which means you can't have a while(true) loop inside doIt()
   // which instead will get called every 1 ms by your init code above.
}

void MyThread::doIt(){
    cout << "it works";
}

Вот соответствующий фрагмент документации, о которой не упоминалось ни один из других плакатов:

int QCoreApplication:: exec()

Вступает в цикл основного события и ждет вызова exit(). Возвращает значение, которое было установлено для выхода() (которое равно 0, если exit() вызывается через уволиться()). Эту функцию необходимо вызвать для запуска события обработки. Основной цикл события принимает события из оконной системы и отправляет их в виджеты приложения. Чтобы сделать приложение выполняет обработку бездействия (т.е. выполняет специальную функцию всякий раз, когда нет ожидающих событий), используйте QTimer с таймаутом 0. Более продвинутые схемы обработки бездействия могут быть достигнуты с использованием processEvents().

Ответ 6

Я создал пример, который вызывает таймер в лямбда-функции:

#include <QCoreApplication>
#include <QObject>
#include <QTimer>
#include <QThread>
#include <QDebug>
#include <memory>

int main(int argc, char** argv)
{
    QCoreApplication app(argc, argv);

    QThread* thread = new QThread(&app);
    QObject::connect(thread, &QThread::started, [=]()
    {
        qInfo() << "Thread started";
        QTimer* timer1 = new QTimer(thread);
        timer1->setInterval(100);
        QObject::connect(timer1, &QTimer::timeout, [=]()
        {
            qInfo() << "Timer1 " << QThread::currentThreadId();
        });
        timer1->start();
    });
    thread->start();

    QTimer timer2(&app);
    QObject::connect(&timer2, &QTimer::timeout, [=]()
    {
        qInfo() << "Timer2 " << QThread::currentThreadId();
    });
    timer2.setInterval(100);
    timer2.start();

    return app.exec();
}