我想在 QThread 内运行一个计时器。我编写了一些代码,在运行时出现一些错误。我做错了什么?
(父线程是QThread(0x1498d10),父线程是QThread(0x11272b0),当前线程是QThread(0x1498d10)
#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
#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;
}
#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
#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 本身对线程一无所知。它只是做它必须做的事情。增加值并显示结果(加上一些睡眠部分以显示它如何阻止其他功能或 GUI)
头文件
#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);
非阻塞GUI
我们还有两个线程正在运行。为什么不使用它们。在示例中,QThreads 已经在运行,但它们什么也没玩。让我们将实例移到那里,以确保 GUI 所在的主循环不会再被阻塞。
神奇的功能是moveToThread
取消注释行,您可以看到,GUI 不会被阻止。两个实例都在新线程中。但话又说回来,有一个睡眠功能,所以一个人应该比另一个人计数得更快。但事实并非如此。因为他们互相阻碍。他们在同一个线程中。
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
所以将它们移至单独的线程
现在 GUI 不再被阻塞,不再互相实例。
mySleepClass->moveToThread(thread1);
myClass->moveToThread(thread2);
结果应该是:(并且 GUI 不应该被阻止)
"sleeper class" count: 1
"normal class" count: 1
"normal class" count: 2
"normal class" count: 3
"normal class" count: 4
"normal class" count: 5
希望这是可以理解的。对我来说,这比子类化更符合逻辑。
当然你可以在你的MyClass中创建QThread,没有必要在MyClass之外创建它,我只是想展示,你可以创建一个线程并移动更多实例。
对于任何不同意的人,我只想说:MyClass是具有线程支持的计数器听起来更好:MyClass是具有计数器能力的线程:)
您的计时器不属于您的线程。您应该在 run() 方法中创建它,或者您应该在将其连接到插槽之前、但在线程启动之后调用timer->moveToThread。
检查一下:MyThread 属于你的主线程。您在 MyThread 的构造函数中创建一个计时器 - 因此该计时器也属于主线程。但是您正在尝试在属于另一个线程的 ::run 方法中初始化并使用它。
为了做到这一点,您的线程中需要有一个事件循环。
来自
QTimer
的 手册页:
在多线程应用程序中,您可以在任何具有事件循环的线程中使用 QTimer。要从非 GUI 线程启动事件循环,请使用 QThread::exec()。 Qt 使用计时器的线程关联来确定哪个线程将发出 timeout() 信号。因此,您必须在其线程中启动和停止计时器;不可能从另一个线程启动计时器。
来自 QThread 的 手册页:
int QThread::exec () [protected]
进入事件循环并等待直到 exit() 被调用,返回传递给 exit() 的值。如果通过 quit() 调用 exit(),则返回值为 0。 有必要调用此函数来启动事件处理。
此外,您的班级中需要有
Q_OBJECT
:
class MyThread:public QThread
{
Q_OBJECT
最后,正如 Dmitry 指出的,您需要在线程内创建一个 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;
}
另外,请阅读本文档。
我能够创建一个简单的示例,使用 lambda 函数在另一个线程中启动计时器。这是代码:
#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();
}