Я хотел бы запустить таймер внутри QThread. Я написал некоторый код, в котором я получаю некоторые ошибки во время выполнения. Пожалуйста, ведите меня в правильном направлении. Что я делаю неправильно?
(Parent is QThread(0x1498d10), parent's thread is QThread(0x11272b0), current thread is QThread(0x1498d10)
mainwindow.h // основной .h файл
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include "mythread.h"namespace Ui {
class MainWindow;
}
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = 0);
~MainWindow();
MyThread *myt;
private:
Ui::MainWindow *ui;
};
#endif // MAINWINDOW_H
mainwindow.cpp // основной файл .cpp
#include "mainwindow.h"#include "ui_mainwindow.h"
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
myt=new MyThread();
myt->start();
MainWindow w;
}
MainWindow::~MainWindow()
{
delete ui;
}
mythread.h // класс для потока
#ifndef MYTHREAD_H
#define MYTHREAD_H
#include <QThread>
#include <QTimer>
class MyThread:public QThread
{
public:
MyThread();
void run();
QTimer *thr;
public slots:
void slo();
};
#endif // MYTHREAD_H
mythread.cpp
#include "mythread.h"
MyThread::MyThread()
{
thr= new QTimer();
connect(thr,SIGNAL(timeout()),this,SLOT(slo()));
}
void MyThread::run()
{
thr->start(1000);
}
void MyThread::slo()
{
int i,j=0;
i=i+j;
}
Просто мое скромное мнение — не подкласс QThread больше, когда тебе не нужно.
Я думаю, вы просто хотите запустить свой класс в новом потоке или, скорее всего, вы не хотите блокировать другие задачи. Ваш класс не сам по себе. Подклассы в основном означают, что ваш класс — это то, что вы подклассифицируете.
Другими словами: Пусть QThread сделает свою работу и сконцентрируется на вашем классе, чтобы сделать то, что он должен делать.
Пример: Сам MyClass ничего не знает о потоках. Просто делай то, что должен. Увеличение значения и отображение результатов (плюс некоторая часть сна, чтобы показать, как он может блокировать другие функции или графический интерфейс)
Заголовочный файл
#include <QTimer>
#include <QObject>
class MyClass : public QObject
{
Q_OBJECT
public:
explicit MyClass(bool willSleep, QString name, QObject *parent = 0);
public slots:
void updateCount();
private:
QTimer *timer;
int count;
bool m_wantToSleep;
};
Реализация
#include "myclass.h"#include <QDebug>
MyClass::MyClass(bool wantToSleep, QString name, QObject *parent) :
QObject(parent)
{
this->setObjectName(name);
m_wantToSleep = wantToSleep;
count = 0;
timer = new QTimer(this);
connect(timer, SIGNAL(timeout()), this, SLOT(updateCount()));
timer->start(100);
}
void MyClass::updateCount()
{
++count;
qDebug() << objectName() << " count: " << count;
if (m_wantToSleep)
sleep(1);
}
У нас есть код, который делает работу.
Теперь реализуйте больше потоков — это очень просто (управление памятью и т. д. не обрабатывается, чтобы иметь простой пример)
#include "mainwindow.h"#include "ui_mainwindow.h"
#include <QThread>
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
QThread *thread1 = new QThread; // First thread
QThread *thread2 = new QThread; // Second thread
thread1->start();
thread2->start();
MyClass *myClass = new MyClass(false, "normal class");
MyClass *mySleepClass = new MyClass(true, "sleeper class");
// Better to implement start slot to start timer ( not implemented )
// connect(thread1, SIGNAL(started), myClass, SLOT(start()));
// but this suffice, because timer will emit first signal after class is moved to another thred
//mySleepClass->moveToThread(thread1);
//myClass->moveToThread(thread1);
}
MainWindow::~MainWindow()
{
delete ui;
}
Теперь мы можем играть с темами:
Блокировка GUI (конечно, мы этого не хотим)
Исходный пример работает без использования новых потоков. Объекты находятся в текущем потоке, и поэтому GUI будет заблокирован. (так как я использую функцию сна в одном случае)
//mySleepClass->moveToThread(thread1);
//myClass->moveToThread(thread1);
Неблокирующий графический интерфейс
У нас работает еще два потока. Почему бы не использовать их. В примере QThreads уже запущены, но они играют ни с чем. Давайте переместим туда наши экземпляры, чтобы основной цикл, в котором находится GUI, больше не блокировался.
Волшебная функция moveToThread
Раскомментируйте строки и вы увидите, что графический интерфейс не будет заблокирован. Оба экземпляра находятся в новой теме. Но опять же, есть функция сна, поэтому один должен считать быстрее, чем другой. Но это не так. Потому что они блокируют друг друга. Они в одной теме.
mySleepClass->moveToThread(thread1);
myClass->moveToThread(thread1);
Результаты в обоих предыдущих случаях должны быть: (экземпляры живут в одном потоке и используют один и тот же цикл событий, поэтому они блокируют друг друга)
"normal class" count: 1
"sleeper class" count: 1
"normal class" count: 2
"sleeper class" count: 2
"normal class" count: 3
"sleeper class" count: 3
Так что перенесите их в отдельную ветку
Теперь графический интерфейс не заблокирован, нигде не встречаются.
mySleepClass->moveToThread(thread1);
myClass->moveToThread(thread2);
Результаты должны быть: (и графический интерфейс не должен быть заблокирован)
"sleeper class" count: 1
"normal class" count: 1
"normal class" count: 2
"normal class" count: 3
"normal class" count: 4
"normal class" count: 5
Надеюсь это было понятно. На мой взгляд, это более логичный подход, чем подклассы.
Конечно, вы можете создать QThread в своем MyClass, нет необходимости создавать его, MyClass, я просто хотел показать, что вы можете создать один поток и перемещать туда больше экземпляров.
Для тех, кто не согласен, я просто хотел сказать, что: MyClass — счетчик с поддержкой потоков звучит лучше тогда: MyClass — это нить со счетчиком 🙂
Ваш таймер не принадлежит вашей теме. Вы должны создать его в своем методе run () или вызвать tmer-> moveToThread перед подключением к слотам, но после запуска потока.
Проверьте это: MyThread принадлежит вашей основной теме. Вы создаете таймер в конструкторе MyThread — так что таймер тоже принадлежит основному потоку. Но вы пытаетесь инициализировать и использовать его в методе :: run, который принадлежит другому потоку.
Для этого вам нужно иметь цикл обработки событий в вашем потоке.
От QTimer
«s справочная страница:
В многопоточных приложениях вы можете использовать QTimer в любом потоке, в котором есть цикл обработки событий. Чтобы запустить цикл обработки событий из потока без графического интерфейса, используйте QThread :: exec (). Qt использует сходство потоков таймера, чтобы определить, какой поток испустит сигнал timeout (). Из-за этого вы должны запустить и остановить таймер в его потоке; невозможно запустить таймер из другого потока.
Из QThread’s справочная страница:
int QThread::exec () [protected]
Входит в цикл обработки событий и ожидает вызова метода exit (), возвращая значение, переданное функции exit (). Возвращаемое значение равно 0, если exit () вызывается через quit ().
Необходимо вызвать эту функцию, чтобы начать обработку событий.
Кроме того, вам нужно иметь Q_OBJECT
в вашем классе:
class MyThread:public QThread
{
Q_OBJECT
И, наконец, как заметил Дмитрий, вам нужно создать QTimer внутри вашего потока, поэтому весь файл cpp должен выглядеть так:
#include "mythread.h"
MyThread::MyThread()
{
}
void MyThread::run()
{
thr= new QTimer();
connect(thr,SIGNAL(timeout()),this,SLOT(slo()));
thr->start(1000);
exec();
}
void MyThread::slo()
{
int i = 0,j=0;
i=i+j;
}
Также читайте этот документ.
Мне удалось создать простой пример, который запускает таймер в другом потоке, используя лямбда-функции. Вот код:
#include <QCoreApplication>
#include <QObject>
#include <QTimer>
#include <QThread>
#include <QDebug>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();
}