简体   繁体   English

如何在QThread中运行计时器?

[英]How to run a timer inside a QThread?

I would like to run a timer inside a QThread. 我想在QThread中运行一个计时器。 I have written some code in which i am getting some error during the run time. 我写了一些代码,其中我在运行时遇到了一些错误。 Please guide me into the right direction. 请引导我走向正确的方向。 What am I doing wrong? 我究竟做错了什么?

(Parent is QThread(0x1498d10), parent's thread is QThread(0x11272b0), current thread is QThread(0x1498d10)

mainwindow.h //main .h file 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 //main .cpp file 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 // class for thread 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 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;
}

Just my humble opinion - Do not to subclass QThread anymore, when you do not need to. 只是我的拙见 -当你不需要时,不要再继承QThread了。

I think, you just want to run your class in new thread or more probably you do not want to block other tasks. 我想,你只想在新线程或更多线程中运行你的类,可能你不想阻止其他任务。 Your class is not thread itself. 你的课不是自己的线程。 Subclassing basically means that your class IS what you are subclassing. 子类化基本上意味着你的类是你的子类。

In other words: Let QThread do its job and concentrate on your class to do what it should do. 换句话说:让QThread完成它的工作并专注于你的课程,做它应该做的事情。

Example: MyClass itself does not know anything about threads. 示例: MyClass本身对线程一无所知。 It just do what it has to do. 它只是做它必须做的事情。 Incrementing value and showing results ( plus some sleep part to show how it can block other functions or gui ) 增加值并显示结果(加上一些睡眠部分以显示它如何阻止其他功能或gui)

Header file 头文件

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

};

Implementation 履行

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

We have code which does the job. 我们有代码来完成这项工作。

Now implement more threads - its very simple ( memory management, etc not handled to have simple example ) 现在实现更多的线程 - 它非常简单 (内存管理等没有处理过简单的例子)

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

Now we can play with threads: 现在我们可以玩线程了:

Blocking GUI ( of course we do not want this ) 阻止GUI(当然我们不希望这样)

Initial example works without using new threads. 初始示例无需使用新线程即可运行。 Objects are in current thread and that's why GUI will be blocked. 对象在当前线程中,这就是GUI被阻止的原因。 ( since I use sleep function in one instance ) (因为我在一个实例中使用sleep函数)

//mySleepClass->moveToThread(thread1);
//myClass->moveToThread(thread1);

Non blocking GUI 非阻塞GUI

We have two more threads running. 我们还有两个线程在运行。 Why not to use them. 为什么不使用它们。 In example QThreads are already running, but they play with nothing. 在示例中,QThreads已经在运行,但它们什么都不玩。 Let's move our instances there, to ensure main loop, where GUI is living will not be blocked anymore. 让我们在那里移动我们的实例,以确保主循环,GUI生存的地方将不再被阻止。

Magic function is moveToThread 魔术功能是moveToThread

Uncomment lines and you can see, that GUI will not be blocked. 取消注释行,您可以看到,GUI不会被阻止。 Both instances are in new thread. 两个实例都在新线程中。 But then again, there is a sleep function so One should be counting faster then other. 但话说回来,有一个睡眠功能,所以一个应该比其他更快。 But it is not. 但事实并非如此。 Because they are blocking each other. 因为他们互相阻挡。 They are in one thread. 他们在一个线程中。

mySleepClass->moveToThread(thread1);
myClass->moveToThread(thread1);

Results in both previous cases should be: ( instances lives in same thread and shares the same event loop, so they are blocking each other ) 以前两种情况的结果应该是:(实例位于同一个线程中并共享相同的事件循环,因此它们相互阻塞)

"normal class"  count:  1 
"sleeper class"  count:  1 
"normal class"  count:  2 
"sleeper class"  count:  2 
"normal class"  count:  3 
"sleeper class"  count:  3 

So move them to separate thread 所以将它们移到单独的线程

Now GUI is not blocked, niether instances each other. 现在GUI没有被阻止,彼此没有实例。

mySleepClass->moveToThread(thread1);
myClass->moveToThread(thread2);

Results should be: ( and GUI should not be blocked ) 结果应该是:(不应阻止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 

Hope It was understandable . 希望这是可以理解的 As for me, this is more logic aproach then subclassing. 至于我,这是更多的逻辑方法然后继承。

Of course you can create QThread in your MyClass , it is not necessary to create it oustide MyClass, I just wanted to show, that you can create one thread and move there more instances. 当然你可以在你的MyClass中创建QThread,没有必要创建它oustide MyClass,我只是想表明,你可以创建一个线程并移动更多的实例。

For anyone who disagree, I just wanted to say that: MyClass is counter with thread support sounds better then: MyClass is thread with counter ability :) 对于任何不同意的人,我只是想说: MyClass是反线程支持听起来更好的话: MyClass是具有反击能力的线程 :)

Your timer does not belong to your thread. 您的计时器不属于您的主题。 You should create it in your run() method or you should call tmer->moveToThread before connecting it to slots, but after thread was started. 您应该在run()方法中创建它,或者在将它连接到插槽之前调用tmer-> moveToThread,但是在线程启动之后。

Check it: MyThread belongs to your main thread. 检查一下:MyThread属于你的主线程。 You create timer in constructor of MyThread - so timer belongs to main thread too. 你在MyThread的构造函数中创建计时器 - 所以计时器也属于主线程。 But you are trying to initialize and use it in ::run method, that belongs to other thread. 但是你试图在:: run方法中初始化并使用它,它属于其他线程。

In order to do this, you need to have event loop in your thread. 为此,您需要在线程中使用事件循环。

From QTimer 's man page : 来自QTimer手册页

In multithreaded applications, you can use QTimer in any thread that has an event loop. 在多线程应用程序中,您可以在任何具有事件循环的线程中使用QTimer。 To start an event loop from a non-GUI thread, use QThread::exec(). 要从非GUI线程启动事件循环,请使用QThread :: exec()。 Qt uses the timer's thread affinity to determine which thread will emit the timeout() signal. Qt使用计时器的线程亲和性来确定哪个线程将发出timeout()信号。 Because of this, you must start and stop the timer in its thread; 因此,您必须在其线程中启动和停止计时器; it is not possible to start a timer from another thread. 无法从另一个线程启动计时器。

From QThread's man page : 来自QThread的手册页

int QThread::exec () [protected]

Enters the event loop and waits until exit() is called, returning the value that was passed to exit(). 进入事件循环并等待直到调用exit(),返回传递给exit()的值。 The value returned is 0 if exit() is called via quit(). 如果通过quit()调用exit(),则返回的值为0。 It is necessary to call this function to start event handling. 必须调用此函数来启动事件处理。

Also, you need to have Q_OBJECT in your class: 此外,您需要在您的班级中拥有Q_OBJECT

class MyThread:public QThread
{
    Q_OBJECT

And finally, as Dmitry noted, you need to create QTimer inside your thread, so the entire cpp file should look like this: 最后,正如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;
}

Also, read this document . 另外,请阅读本文档

I was able to create a simple example that starts a timer within another thread, using lambda functions. 我能够创建一个简单的例子,使用lambda函数在另一个线程中启动一个计时器。 Here is the code: 这是代码:

#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();
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM