如何从另一个线程正确停止 QTimer

How to properly stop QTimer from another thread

我看到这样的话题被讨论了很多次,但找不到简单情况的明确答案。我在创建计时器的自己的线程中有 Worker class 运行 并且由于某些情况想要停止它。 但我收到错误:

Timers cannot be stopped from another thread

我补充说在 Qt 中工作的线程中缺少一些核心逻辑。有人可以解释如何解决吗?谢谢。

这是main.cpp

#include <QCoreApplication>
#include <QObject>
#include <QtDebug>

#include "worker.h"
int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);

    Worker w;

    return a.exec();
}

Worker.h

#ifndef WORKER_H
#define WORKER_H

#include <QObject>
#include <QTimer>
#include <QThread>
#include <QtDebug>

class Worker : public QObject
{
    Q_OBJECT
public:
    explicit Worker(QObject *parent = nullptr);
private:
    QThread t;
    QTimer *timer;

    int count = 1;

public slots:

    void dataTimerFunction();
    void onStopTimer(QTimer *t);
signals:
    void stopTimer(QTimer *t);
};

#endif // WORKER_H

Worker.cpp

#include "worker.h"

Worker::Worker(QObject *parent) : QObject(parent)
{
    this->moveToThread(&t);
    QObject::connect(&t, &QThread::finished, this, &QObject::deleteLater);

    t.start();

    // are we in the new thread from this point, right?
    timer = new QTimer();
    QObject::connect(timer, &QTimer::timeout, this, &Worker::dataTimerFunction);
    QObject::connect(this, &Worker::stopTimer, this, &Worker::onStopTimer);
    // QObject::connect(this, &Worker::stopTimer, this, &Worker::onStopTimer, Qt::QueuedConnection);  doesn't work as well
    timer->start(200);
}

void Worker::dataTimerFunction()
{
    qDebug()<<count;
    count++;
    if (count>5){
        emit stopTimer(timer);
        //timer->stop();
    }
}

void Worker::onStopTimer(QTimer *t)
{
    t->stop();
}

问题确实是计时器没有移到你的线程。

这是一个经过修改的工作程序 class,它可以实现您可能想要实现的目标。它将定时器也移动到线程,然后通过使用信号/槽来启动和停止它。

worker.h

#ifndef WORKER_H
#define WORKER_H

#include <QObject>
#include <QTimer>
#include <QThread>
#include <QtDebug>

class Worker : public QObject
{
    Q_OBJECT
public:
    explicit Worker(QObject *parent = nullptr);
private:
    QThread t;
    QTimer *timer;

    int count = 1;

signals:
    void stopTimer();
    void startTimer(int msec);

public slots:

    void dataTimerFunction();
};

#endif // WORKER_H

worker.cpp

#include "worker.h"

Worker::Worker(QObject *parent) : QObject(parent)
{
    // are we in the new thread from this point, right?
    timer = new QTimer(nullptr);

    this->moveToThread(&t);
    timer->moveToThread(&t);
    QObject::connect(&t, &QThread::finished, this, &QObject::deleteLater);
    t.start();

    QObject::connect(timer, &QTimer::timeout, this, &Worker::dataTimerFunction);
    QObject::connect(this, &Worker::stopTimer, timer, &QTimer::stop, Qt::QueuedConnection);  
    QObject::connect(this, &Worker::startTimer, timer, static_cast<void (QTimer::*)(int)>(&QTimer::start), Qt::QueuedConnection);  
    startTimer(200);
}



void Worker::dataTimerFunction()
{
    qDebug()<<count;
    count++;
    if (count>5){
        stopTimer();
    }
}

您应该在您的任务 object 槽中新建您的计时器,而不是在任务构造器中。当您的任务 object 被删除时停止计时器。

我的任务objectheader:Worker.h

#pragma once
#include <QObject>
class QTimer;

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

public slots:
    void WorkerTaskStartSlot(void);
    void TaskFinished(void);
private slots:
    void TimerOutToDoSomethingSlot(void);
signals:
    void WorkertResultSig(void);

private:
    QTimer *m_pTimer = nullptr;
};

Worker.cpp:

#include "Worker.h"
#include <QDebug>
#include <QThread>
#include <QTimer>

Worker::Worker(QObject *parent) : QObject(parent)
{
    qDebug()<<__FUNCTION__<<"threadid"<< QThread::currentThreadId();

}

Worker::~Worker()
{
    TaskFinished();
      qDebug()<<__FUNCTION__<<"threadid"<< QThread::currentThreadId();
}


void Worker::WorkerTaskStartSlot(void)
{
    qDebug()<<__FUNCTION__<<"threadid"<< QThread::currentThreadId();
    emit WorkertResultSig();

    m_pTimer = new QTimer(this);
    connect(m_pTimer,&QTimer::timeout,this,&Worker::TimerOutToDoSomethingSlot);
    m_pTimer->start(1000);
}


void Worker::TaskFinished(void)
{
    qDebug()<<__FUNCTION__<<"threadid"<< QThread::currentThreadId();

    if(m_pTimer)
    {
        if(m_pTimer->isActive())
        {
            m_pTimer->stop();
            qDebug()<<__FUNCTION__<<"stop timer"<< QThread::currentThreadId();
        }

        delete m_pTimer;
        m_pTimer = nullptr;
    }
}


void Worker::TimerOutToDoSomethingSlot(void)
{
    qDebug()<<__FUNCTION__<<"threadid"<< QThread::currentThreadId();
}

控制器header:MainWindow.h

#pragma once

#include <QMainWindow>
class QPushButton;
class QWidget;
class Worker;
class QThread;

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    MainWindow(QWidget *parent = nullptr);
    ~MainWindow();
private:
    void InitCtrl(void);
private slots:
    void StartTaskBtnSlot(const bool &checked);
    void WorkertResultSlot(void);
private:
    QPushButton *m_pStartTaskBtn = nullptr;
    QWidget *m_pCenterWidget = nullptr;
    Worker *m_pWorker = nullptr;
    QThread *m_pWorkerThread = nullptr;
};

MainWindow.cpp

#include "MainWindow.h"
#include <QVBoxLayout>
#include <QPushButton>
#include <QWidget>
#include <QDebug>
#include <QThread>
#include "Worker.h"

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
{
    InitCtrl();
    qDebug()<<__FUNCTION__<<"mainwindow thread id"<< QThread::currentThreadId();
}

MainWindow::~MainWindow()
{
}

void MainWindow::InitCtrl(void)
{
    m_pCenterWidget = new QWidget(this);
    m_pStartTaskBtn = new QPushButton(QStringLiteral("Start"),this);

    QVBoxLayout *pvertlayout = new QVBoxLayout();
    pvertlayout->addWidget(m_pStartTaskBtn);
    m_pCenterWidget->setLayout(pvertlayout);

    setCentralWidget(m_pCenterWidget);

    m_pStartTaskBtn->setCheckable(true);
    connect(m_pStartTaskBtn,&QPushButton::clicked,this,&MainWindow::StartTaskBtnSlot);

}

void MainWindow::StartTaskBtnSlot(const bool &checked)
{
    if(checked)
    {
        m_pStartTaskBtn->setText(QStringLiteral("Close"));

        m_pWorkerThread = new QThread();
        m_pWorker = new Worker();

        // move the task object to the thread BEFORE connecting any signal/slots
        m_pWorker->moveToThread(m_pWorkerThread);

        connect(m_pWorkerThread, SIGNAL(started()), m_pWorker, SLOT(WorkerTaskStartSlot()));
        connect(m_pWorker, SIGNAL(WorkertResultSig()), this, SLOT(WorkertResultSlot()));

        // automatically delete thread and task object when work is done:
        connect(m_pWorkerThread, SIGNAL(finished()), m_pWorker, SLOT(deleteLater()));
        connect(m_pWorkerThread, SIGNAL(finished()), m_pWorkerThread, SLOT(deleteLater()));


        m_pWorkerThread->start();
    }
    else
    {
        m_pStartTaskBtn->setText(QStringLiteral("Start"));
        m_pWorkerThread->quit();
        m_pWorkerThread->wait();
    }
}

void MainWindow::WorkertResultSlot(void)
{
    qDebug()<<__FUNCTION__<<"threadid"<<QThread::currentThreadId();

}

最后会输出这样的结果:

MainWindow::MainWindow mainwindow thread id 0x2bf0
Worker::Worker threadid 0x2bf0
Worker::WorkerTaskStartSlot threadid 0x4af0
MainWindow::WorkertResultSlot threadid 0x2bf0
Worker::TimerOutToDoSomethingSlot threadid 0x4af0
Worker::TimerOutToDoSomethingSlot threadid 0x4af0
Worker::TimerOutToDoSomethingSlot threadid 0x4af0
Worker::TimerOutToDoSomethingSlot threadid 0x4af0
Worker::TaskFinished threadid 0x4af0
Worker::TaskFinished stop timer 0x4af0
Worker::~Worker threadid 0x4af0