[Qt] QObject::startTimer: Timers cannot be started from another threadQt多线程中使用QTimer和信号、槽

多线程中使用QTimer

我们可能在Qt的多线程中使用QTimer中都会遇到一个错误:

Cannot create children for a parent that is in a different thread.
或者
QObject::startTimer: Timers cannot be started from another thread

QTimer定时器不能在不同的线程中启动。
出现这个主要原因是没有很好的将主线程和子线程进行区分。
我们看以下的多线程代码:(在Qt中的多线程)
这个是定义一个类继承自QThread,并且重写其中的虚函数run。之后,启动线程run函数就在子线程中运行了。
注意:只有run函数的函数体中的内容是在多线程中运行的。MyThread的成员函数都属于主线程。

#ifndef MYTHREAD_H
#define MYTHREAD_H
#include <QThread>
#include <QTimer>

class MyThread : public QThread
{
    Q_OBJECT
public:
    MyThread();
    virtual void run()override;
    QTimer* timer;

signals:
    void TestThread_Signal();   //自定义的信号
public slots:
    void TestThread_Slot();
    void wode();
};

#endif // MYTHREAD_H

错误代码1:

QObject::startTimer: Timers cannot be started from another thread
出现这个原因:主要是在主线程中进行Qtimer在堆中的内存分配,因此,该定时器属于同一个线程,如果在子线程中进行start,那么就会在别的线程中开启主线程中的定时器。Qt不允许这样操作。

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    qDebug()<<"Main ThreadID"<< this->thread()->currentThreadId();
//    t1.moveToThread(&t1);
    t1.timer = new QTimer();
    t1.timer->setInterval(90);
    t1.start();
}
void MyThread::run()
{
    connect(timer, &QTimer::timeout, this, &MyThread::wode);
    timer->start();
    this->exec();//这个是Qt的消息循环,只有加这个才可以监听信号
}

更改代码1:

将定时器的在堆中的函数

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    qDebug()<<"Main ThreadID"<< this->thread()->currentThreadId();
//    t1.moveToThread(&t1);
    t1.start();
}
void MyThread::run()
{
//    connect(&timer,&QTimer::timeout,this,[](){qDebug()<<"Timer goes";});
    //不能加this
    timer = new QTimer();
    timer->setInterval(90);
    connect(timer, &QTimer::timeout, this, &MyThread::wode);
    timer->start();
    //一定要有exec()
    this->exec();
}

运行结果:
可以看出槽函数在主线程中运行,这也就说明了t1对象属于主线程。
在这里插入图片描述

错误代码2:

经历了上面的那个例子,我们可以看出主线程和子线程的区别之处。那么,我们根据下面这个例子再forward。。。
啊哦~~~报警告了。

Cannot create children for a parent that is in a different thread.

这是为什么呢?
经过我们之前的分析,t1也即是那个线程类对象在主线程中,只有run函数体的部分属于子线程,那么,QTimer现在在子线程new对象,QTimer指向的内存即是子线程中,
(注意:timer是在主线程中,因为它是栈区的。)
你却将this(t1)传为它的父对象,怎么可能不报错呢???
因此,这既是有的博客说的千万不要加this的用意之处。我们可以通过以下代码进行测试:

if(this->thread() == timer->thread())
    {
        qDebug()<<"same";
    };
MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    qDebug()<<"Main ThreadID"<< this->thread()->currentThreadId();
//    t1.moveToThread(&t1);
    t1.start();
}
void MyThread::run()
{
//    connect(&timer,&QTimer::timeout,this,[](){qDebug()<<"Timer goes";});
    //不能加this
    timer = new QTimer(this);
    timer->setInterval(90);
    connect(timer, &QTimer::timeout, this, &MyThread::wode);
    timer->start();
    //一定要有exec()
    this->exec();
}

更改代码2:

用到了movetoThread函数进行。

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    qDebug()<<"Main ThreadID"<< this->thread()->currentThreadId();
    t1.moveToThread(&t1);
    t1.start();
}
void MyThread::run()
{
    timer = new QTimer(this);
    if(this->thread() == timer->thread())
    {
        qDebug()<<"same";
    };
    timer->setInterval(90);
    connect(timer, &QTimer::timeout, this, &MyThread::wode);
    timer->start();
    //一定要有exec()
    this->exec();
}

运行结果:我们可以看出当通过moveToThread函数之后,我们就可以进行。
注意:从测试案例中我们可以看出。槽函数也是打印的子线程的线程号,因此,moveToThread函数会将t1移动到子线程中,new定时器的适合加this也问题不大了。因为,父子对象处于一个线程了。
在这里插入图片描述

多线程中使用信号和槽

参考博客:
https://www.cnblogs.com/JayChen1996/p/8688603.html

经过上面的分析,我们对子线程和主线程的区分已经了解了。因此,在多线程中使用信号和槽就很容易了。

两点需要注意:
1、需要添加Q_OBJECT宏
2、需要exec()来监听信号

  • 16
    点赞
  • 80
    收藏
    觉得还不错? 一键收藏
  • 11
    评论
### 回答1: 在Qt编写定时器代码非常简单,可以使用 `QTimer` 类实现。以下是一个简单的例子: ```cpp // 创建一个 QTimer 对象 QTimer *timer = new QTimer(this); // 连接定时器的超时信号到一个函数 connect(timer, SIGNAL(timeout()), this, SLOT(update())); // 设置定时器的时间间隔为 1000 毫秒(即 1 秒) timer->start(1000); ``` 在这个例子,我们创建了一个 `QTimer` 对象,并将其超时信号连接到 `this` 对象的 `update()` 函数。然后,我们设置定时器的时间间隔为 1000 毫秒,并启动定时器。 当定时器超时时,它会发出 `timeout()` 信号,该信号被连接到 `update()` 函数。在 `update()` 函数,你可以编写你想要执行的代码。 请注意,你需要在类的头文件声明 `update()` 函数,如下所示: ```cpp class MyClass : public QObject { Q_OBJECT public: MyClass(QObject *parent = nullptr); public slots: void update(); }; ``` 这样就可以在Qt编写定时器代码了。 ### 回答2: 在Qt编写定时器代码非常简单。首先,我们需要在类的头文件包含QTimer和QEvent类的头文件。 ```cpp #include <QTimer> #include <QEvent> ``` 然后,在类的私有成员创建一个QTimer对象。 ```cpp private: QTimer *timer; ``` 接下来,在类的构造函数初始化定时器,并设置其时间间隔。 ```cpp MyClass::MyClass() { // 其他代码... timer = new QTimer(this); timer->setInterval(1000); // 设置定时器间隔(毫秒) } ``` 然后,我们可以连接定时器的timeout信号函数,以实现定时触发。 ```cpp connect(timer, &QTimer::timeout, this, &MyClass::timerEvent); ``` timerEvent是我们自定义的函数,它将在定时器触发时被调用。 ```cpp void MyClass::timerEvent() { // 定时器触发时执行的代码 } ``` 最后,我们在需要启动定时器的地方调用start函数。 ```cpp void MyClass::startTimer() { timer->start(); } ``` 如果我们需要停止定时器,可以简单地调用stop函数。 ```cpp void MyClass::stopTimer() { timer->stop(); } ``` 这样,我们就成功在Qt编写了一个简单的定时器代码。通过设置定时器的时间间隔和连接其timeout信号函数,我们可以实现定时触发特定代码的功能。 ### 回答3: 在Qt编写定时器代码十分简单,可以通过以下几个步骤完成: 1. 首先,在需要使用定时器的类,包含`QTimer`头文件: ```cpp #include <QTimer> ``` 2. 在类的私有部分,声明一个`QTimer`对象: ```cpp private: QTimer *timer; ``` 3. 在类的构造函数,初始化定时器对象: ```cpp MyClass::MyClass() { timer = new QTimer(this); } ``` 4. 连接定时器的`timeout()`信号函数: ```cpp connect(timer, SIGNAL(timeout()), this, SLOT(timerSlot())); ``` 5. 实现函数,该函数会在定时器超时时触发: ```cpp void MyClass::timerSlot() { // 在这里编写定时器超时时要执行的代码 } ``` 6. 设置定时器的触发时间间隔,并启动定时器: ```cpp int interval = 1000; // 设置定时器触发的时间间隔为1秒 timer->start(interval); ``` 通过以上步骤,就可以在Qt编写定时器代码了。注意,在定时器超时时执行的代码块,可以编写任意需要定时触发的操作,比如更新UI界面、执行后台任务、处理定时事件等。同时,需要注意在类的析构函数释放定时器对象的内存: ```cpp MyClass::~MyClass() { delete timer; } ``` 最后,记得在需要使用定时器的地方创建类的对象并调用相应的函数,以开始定时器的工作。
评论 11
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值