QTimer备忘

1、定时器是在当前线程中执行的,实际上,它处于当前线程的事件循环中,如果想定时器放在单独的线程中执行,可以借助于moveToThread实现。

你可以把QTimer看作是一个能够产生定时事件的对象。当你启动一个QTimer时,你实际上是在告诉事件循环:“请在指定的时间后向我发送一个定时器超时事件(Timer Timeout Event)”。当事件循环检测到这个时间已经到达,它就会创建一个定时器超时事件,并将其分发给QTimer对象。然后QTimer对象就会发出一个超时信号(timeout signal),这个信号可以被其他对象捕获并作出相应的响应。

深入理解Qt定时器:QTimer的魅力与挑战(一)-阿里云开发者社区 (aliyun.com)
2、虽然是在当前线程中执行的,但即便把定时器的执行频率设置的非常高,也不会导致当前界面无响应,除非在定时器的处理函数中持续占CPU(例如sleep循环)。
3、假如弄了多个定时器,这些定时器实际上是串行执行的,同一时刻只有一个定时器处理函数在执行。

4、一个主线程和子线程使用各自定时器的例子,如此,两个定时器就可以并发,而且子线程中调用MainWindow的函数即使阻塞也不会影响主线程MainWindow的其它执行。

#ifndef THREADTIMER_H
#define THREADTIMER_H
#include <QObject>
class QThread;
class QTimer;
class MainWindow;

class ThreadTimer : public QObject
{
    Q_OBJECT
public:
    explicit ThreadTimer(QObject *parent = nullptr);
    ~ThreadTimer();

private:
    void OnTimeOut();

public slots:
    void StartTimer(int timeout);
    void StopTimer( );

private:
    QTimer *m_pTimer;
    QThread *m_pCurrThread;
    MainWindow *m_pMainWindow;
};
#endif // THREADTIMER_H
#include "threadtimer.h"
#include <QThread>
#include <QTimerEvent>
#include <QDebug>
#include <QTimer>
#include <QTime>
#include "mainwindow.h"

ThreadTimer::ThreadTimer(QObject *parent) : QObject(parent)
{
    m_pMainWindow = qobject_cast<MainWindow*>(parent);
    m_pCurrThread = new QThread;
    m_pTimer = new QTimer(m_pCurrThread);// 不能传this,否则报QObject::startTimer: Timers can only be used with threads started with QThread
    connect(m_pTimer, &QTimer::timeout, this, &ThreadTimer::OnTimeOut);
    moveToThread(m_pCurrThread);
    m_pCurrThread->start();
}

ThreadTimer::~ThreadTimer()
{
    if (nullptr != m_pCurrThread)
    {
        m_pCurrThread->quit();
        m_pCurrThread->wait();
        m_pCurrThread->deleteLater();
    }
}

void ThreadTimer::StartTimer(int timeout)
{
    m_pTimer->start(timeout * 1000);
}

void ThreadTimer::StopTimer()
{
    m_pTimer->stop();
}

void ThreadTimer::OnTimeOut()
{
    qDebug() << "ThreadTimer::OnTimeOut triggered:" << QTime::currentTime().toString("HH::mm::ss") << " threadId:" << QThread::currentThreadId();
    m_pMainWindow->testBlock();
}

#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QTimer>
#include <QDebug>
#include <QTime>
#include <QThread>
#include "threadtimer.h"

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    testTimer();
}

MainWindow::~MainWindow()
{
    delete ui;
}

void MainWindow::timer1Out()
{
    qDebug() << "timer1 triggered:" << QTime::currentTime().toString("HH::mm::ss") << " threadId:" << QThread::currentThreadId();
    for (int i = 0; i < 5; i++)
    {
        qDebug() << "timer1 sleep" << QTime::currentTime().toString("HH::mm::ss") << " threadId:" << QThread::currentThreadId();
        QThread::sleep(1);
    }
}

void MainWindow::testTimer()
{
    QTimer *timer1 = new QTimer(this);

    connect(timer1, &QTimer::timeout, this, &MainWindow::timer1Out);

    QMenuBar *menubar = menuBar();
    setMenuBar(menubar);

    QMenu * menuTimer = new QMenu("定时器");

    menubar->addMenu(menuTimer);

    QAction *action1 = new QAction("开始定时器");
    QAction *action2 = new QAction("停止定时器");

    menuTimer->addAction(action1);
    menuTimer->addAction(action2);

    connect(action1, &QAction::triggered, this, [=](){
        timer1->start(1000);

        ThreadTimer *threadTimer = new ThreadTimer();
        threadTimer->StartTimer(1);
    });

    connect(action2, &QAction::triggered, this, [=](){
       timer1->stop();
    });
}

void MainWindow::testBlock()
{
    qDebug() << "MainWindow::testBlock:" << QTime::currentTime().toString("HH::mm::ss") << " threadId:" << QThread::currentThreadId();
    for (int i = 0; i < 5; i++)
    {
        qDebug() << "testBlock sleep" << QTime::currentTime().toString("HH::mm::ss") << " threadId:" << QThread::currentThreadId();
        QThread::sleep(1);
    }
}

5、Qt 多线程中使用QTimer和信号、槽 QObject::startTimer: Timers cannot be started from another thread - 一杯清酒邀明月 - 博客园

这篇文章说的大致意思是,QThread对象和子线程运行空间不是一回事,QThread对象本身并不属于子线程空间,因此如果QThread对象是在主线程创建的,那在QThread子类中创建QTimer时传入this,那实际上这个QTimer还是属于QThread对象从而还是属于主线程,它并不能实现在子线程中实现定时器,所以文章说了两种形式:1.在QThread子类中创建QTimer时不传this,结果就是定时器实际上还是运行在主线程中。2.在在QThread子类中创建QTimer时传this,并且,QThread子类对象把自己关联到自己所指示的线程上,即,t1.moveToThread(&t1)。

6、定时器的处理函数不会阻塞定时器的计时,也就是说,定时器计时一直是按正常时间流逝进行的,或者说是准的,但是,假如定时器处理函数处理的时长超过了定时周期,那么这第二次的到期处理函数就不会被调用,那么它就继续下一个周期,也就是2倍周期到期的时候调用,那假如2倍的时候这第一次还没完事呢,那就继续第3个周期的时候........。

7、调用setInterval改变周期会立即生效,比如说,原来是12秒一次,它正计时到3秒呢,你把周期改为5秒一次了,那再过5-3=2秒,它就会立即触发。

8、如果调用了singleShot,之后还没到触发时间又调用了singleShot,那么这两次调用随后都会触发,而且各自按各自的倒计时。为了防止这种情况,有一种替代方案是不用singleShot触发,但是用setSingleShot将定时器设置为单次触发,用法示例:

    QTimer *timerSingle = new QTimer(this);
    connect(timerSingle, &QTimer::timeout, this, &MainWindow::timerSingleOut);
    timerSingle->setSingleShot(true);

。。。。

    connect(actionSingle, &QAction::triggered, this, [=](){
        timerSingle->start(10*1000);
        qDebug() << QTime::currentTime().toString("HH::mm::ss") << ": 开始计时timerSingle";
    });

这样即使连续多次执行了start,定时器也只会被触发一次,不过需要注意的是,它被触发的是时刻是从最后那一次start计时的,实际上就相当于前面执行的start都被忽略了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值