5

我想在 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

我的线程.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;
}
4

4 回答 4

11

只是我的拙见—— 当你不需要的时候,不要再继承 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);

非阻塞图形用户界面

我们还有两个线程在运行。为什么不使用它们。在示例中,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 是具有计数器能力的线程:)

于 2013-09-23T13:47:24.453 回答
6

您的计时器不属于您的线程。你应该在你的 run() 方法中创建它,或者你应该在将它连接到插槽之前调用 tmer->moveToThread,但在线程启动之后。

检查它: MyThread 属于您的主线程。您在 MyThread 的构造函数中创建计时器 - 所以计时器也属于主线程。但是您正在尝试在属于其他线程的 ::run 方法中初始化和使用它。

于 2013-09-23T11:56:23.457 回答
4

为了做到这一点,你需要在你的线程中有事件循环。

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;
}

另外,请阅读此文档

于 2013-09-23T11:51:16.217 回答
0

我能够创建一个简单的示例,该示例使用 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();
}
于 2018-04-23T14:43:00.543 回答