解决QTimer报“Timers cannot be started from another thread“错误

    今天在Qt编程时,将QTimer在子线程里执行start()函数,遇到“Timers cannot be started from another thread”问题,使用了如下AI工具,进行查询:
    提示词A:“C++ QTimer 如何跨线程”
    提示词B:“C++ QTimer QThread::run 执行”
    提示词C:“C++ QThread::run 在start()之后 会自动退出吗”

问题原因:QTimer本身不支持跨线程调用
解决方法:QTimer的运行,需要它所在的线程支持事件循环,若没有事件循环,则调用QTimer::start()语句时,会报"Timers cannot be started from another thread"错误,即QTimer失效。
    所以,要想让QTimer在子线程里正常运行,则需要把该子线程的事件循环开启即可。

注意:

  • 若QThread::run()没有被重写override,则默认是开启了事件循环。
  • Qt的主线程,也默认开启了事件循环;
  • C++ std::thread默认没有事件循环;

1 米塔AI的回答

密塔AI官网: https://metaso.cn/

1.1 在QThread::run()函数里执行QTimer

    在子线程中创建和使用QTimer:确保QTimer对象在子线程中创建,并且其信号和槽函数也在子线程中处理。
    // 代码: codeA

   void WorkerThread::run(){QTimer timer;connect(&timer, &QTimer::timeout, this, &WorkerThread::onTimerTick);timer.start(1000); // 每秒触发一次 timeout 信号exec(); // 启动事件循环}

    codeA的含义是,在QThread::run()里创建一个QTimer对象,然后,也在run()里执行这个QTimer,
并通过exec()启动事件循环。

1.2 将QTimer对象移动到子线程

    创建一个继承自QObject的类,比如Worker类,并在其中创建QTimer对象,然后将该对象移动到子线程中执行。例如:

    // 代码: codeB

   class Worker : public QObject{Q_OBJECTprivate slots:void onTimeout(){qDebug() << "Worker::onTimeout get called from?: " << QThread::currentThreadId();}};int main(int argc, char *argv[]){QApplication a(argc, argv);qDebug() << "From main thread: " << QThread::currentThreadId();QThread t;QTimer timer;Worker worker;QObject::connect(&timer, &QTimer::timeout, &worker, &Worker::onTimeout);timer.start(1000);worker.moveToThread(&t);t.start();return a.exec();}

1.3 米塔AI的总结

    codeA和codeB,都是把QTimer的执行环境,放到子线程里,

  • 方法A: 通过在QThread::run()里,新建QTimer对象,让QTimer的执行环境处于子线程中,并在子线程里启动事件循环;
  • 方法B: 通过moveThread()函数,把QTimer的执行环境,从主线程移动到子线程;
    然后,再调用QTimer::start()函数,即可运行定时器,从而解决"Timers cannot be started from another thread"问题

2 DeepSeek的回答

    DeepSeek官网: https://chat.deepseek.com/
    其他版本的DeepSeek,比如超算中心DeepSeek网站: https://chat.scnet.cn/#/home

2.1 在QThread::run()函数里执行QTimer

    在 C++ 中使用 QTimer 和 QThread 时,如果想在 QThread::run() 中执行 QTimer,则需要注意的是QTimer 依赖于事件循环。

  • 若QThread::run()被重写override,则需要手动提供事件循环QEventLoop ,即在run()函数里要加上exec()或者loop.exec()语句。
  • 若QThread::run()没有被重写override,则不需要手动提供事件循环QEventLoop 。

    以下是一个示例,展示如何在 QThread::run() 中执行 QTimer:

#include <QCoreApplication>
#include <QThread>
#include <QTimer>
#include <QDebug>
#include <QEventLoop>class WorkerThread : public QThread
{Q_OBJECTprotected:void run() override{qDebug() << "WorkerThread started in thread:" << QThread::currentThreadId();// 创建一个 QTimerQTimer timer;connect(&timer, &QTimer::timeout, this, &WorkerThread::onTimeout);// 启动定时器,每隔 1 秒触发一次timer.start(1000);// 创建一个事件循环以保持线程运行QEventLoop loop;connect(this, &WorkerThread::finished, &loop, &QEventLoop::quit); // 线程结束时退出事件循环loop.exec(); // 进入事件循环qDebug() << "WorkerThread finished.";}private slots:void onTimeout(){qDebug() << "Timeout occurred in thread:" << QThread::currentThreadId();}
};int main(int argc, char *argv[])
{QCoreApplication a(argc, argv);WorkerThread workerThread;qDebug() << "Main thread:" << QThread::currentThreadId();// 启动工作线程workerThread.start();// 5 秒后停止工作线程QTimer::singleShot(5000, [&workerThread]() {qDebug() << "Stopping worker thread...";workerThread.quit(); // 退出线程的事件循环workerThread.wait();  // 等待线程结束qDebug() << "Worker thread stopped.";QCoreApplication::quit(); // 退出主事件循环});return a.exec();
}

2.2 将QTimer对象移动到子线程

    通常,QTimer 在主线程中创建,但可以通过信号和槽机制在另一个线程中控制它。

  • a) 在主线程中创建 QTimer;
  • b) 将 QTimer通过moveThread()函数,移动到目标线程(也称子线程);
  • c) 使用信号和槽进行跨线程通信;
    示例代码如下:
#include <QCoreApplication>
#include <QTimer>
#include <QThread>
#include <QDebug>class Worker : public QObject
{Q_OBJECTpublic:Worker(QObject *parent = nullptr) : QObject(parent){m_timer = new QTimer(this);connect(m_timer, &QTimer::timeout, this, &Worker::onTimeout);}void startTimer(){m_timer->start(1000); // 1秒触发一次}void stopTimer(){m_timer->stop();}public slots:void onTimeout(){qDebug() << "Timeout occurred in thread:" << QThread::currentThreadId();}private:QTimer *m_timer;
};int main(int argc, char *argv[])
{QCoreApplication a(argc, argv);QThread workerThread;Worker worker;// 将 worker 移动到 workerThreadworker.moveToThread(&workerThread);// 启动线程workerThread.start();// 使用信号和槽启动定时器QTimer::singleShot(0, &worker, &Worker::startTimer);// 5秒后停止定时器并退出线程QTimer::singleShot(5000, [&]() {worker.stopTimer();workerThread.quit();workerThread.wait();qDebug() << "Timer stopped, thread finished.";a.quit();});return a.exec();
}

关键点

  1. moveToThread: 将 Worker 对象移动到新线程,确保 QTimer 在新线程的事件循环中运行。
  2. 信号和槽: 使用 QTimer::singleShot 和信号槽机制跨线程控制 QTimer。
  3. 线程安全: QTimer 的操作是线程安全的,前提是它在其所在线程的事件循环中运行。
  4. 通过 moveToThread 和信号槽机制,可以安全地在多线程环境中使用 QTimer,确保定时器在正确的线程中运行。

3 豆包AI的回答

    豆包AI,既有客户端版本,也有网页版本,这里给出的是网页版本的豆包AI地址:
https://www.doubao.com/chat/?channel=browser_landing_page

3.1 在QThread::run()函数里执行QTimer

    在Thread::run()函数里,新建一个QTimer对象,然后,调用QTimer::start(),再启动事件循环exec()。

#include <QCoreApplication>
#include <QTimer>
#include <QThread>
#include <QDebug>// 自定义线程类
class MyThread : public QThread {Q_OBJECT
public:explicit MyThread(QObject *parent = nullptr) : QThread(parent) {}protected:void run() override {QTimer timer;timer.setInterval(1000); // 设置定时器间隔为1秒connect(&timer, &QTimer::timeout, []() {qDebug() << "Timer timeout in thread:" << QThread::currentThreadId();});timer.start();exec(); // 启动线程的事件循环}
};int main(int argc, char *argv[]) {QCoreApplication a(argc, argv);MyThread thread;thread.start();return a.exec();
}#include "main.moc"

3.2 将业务对象(非QTimer)通过moveThread()移动到子线程,

    豆包AI的回答,比前面2个AI: 密塔AI、DeepSeek的回答,更加简简洁。
    它是将纯业务对象Worker移动到子线程,而QTimer仍在主线程,QTimer通过信号槽机制,与业务对象Worker关联起来。

  • 将线程QThread的启动信号start,绑定到QTimer的start()槽函数;
  • 同时,QTimer的超时信号timeout,绑定Worker的handleTimeout()槽函数;

    这种方式,间接的实现了跨线程调用QTimer。
    即子线程thread --> 主线程的timer;

#include <QCoreApplication>
#include <QTimer>
#include <QThread>
#include <QDebug>// 自定义工作类
class Worker : public QObject {Q_OBJECT
public:explicit Worker(QObject *parent = nullptr) : QObject(parent) {}public slots:void handleTimeout() {qDebug() << "Timer timeout in thread:" << QThread::currentThreadId();}
};int main(int argc, char *argv[]) {QCoreApplication a(argc, argv);// 创建工作对象和线程Worker worker;QThread thread;worker.moveToThread(&thread);// 创建定时器QTimer timer;timer.setInterval(1000); // 设置定时器间隔为1秒// 连接信号和槽QObject::connect(&timer, &QTimer::timeout, &worker, &Worker::handleTimeout);QObject::connect(&thread, &QThread::started, &timer, QOverload<>::of(&QTimer::start));// 启动线程thread.start();return a.exec();
}

4 QTimer跨线程调用的总结

方式直接调用QTimer间隔调用QTimer
A在QThread::run()函数里创建QTimer,并运行QTimer,启动事件循环将QTimer对象移动到子线程
B在QThread::run()函数里执行QTimer,并运行QTimer,启动事件循环将业务对象Woker到子线程,然后QTimer的超时信号绑定Worker里的槽函数

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/895241.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

单片机上SPI和IIC的区别

SPI&#xff08;Serial Peripheral Interface&#xff09;和IC&#xff08;Inter-Integrated Circuit&#xff09;是两种常用的嵌入式外设通信协议&#xff0c;它们各有优缺点&#xff0c;适用于不同的场景。以下是它们的详细对比&#xff1a; — 1. 基本概念 SPI&#xff0…

SQL Server安装流程

SQL Server 2022在安全性、可用性和性能方面不断创新&#xff0c;是现在最支持Azure的SQL Server版本。 SQL Server发展史 SQL Server的历史始于1989年&#xff0c;当时是由微软与Sybase合作的产品&#xff0c;旨在为Windows NT操作系统提供一个高性能的数据库解决方案。随着…

从零开始认识大语言模型(LLM)

“AI小美好——聚焦科技、商业、职场。前沿资讯&#xff0c;实用干货&#xff0c;邂逅更美好的自己&#xff01;” 在当今数字化时代&#xff0c;语言不仅是人类交流的工具&#xff0c;更是信息传递的核心。随着人工智能技术的飞速发展&#xff0c;大语言模型逐渐走进了我们的…

安装OpenJDK21(linux、macos)

文章目录 安装OpenJDK21java21linux下安装配置mac下安装 安装OpenJDK21 java21 封神&#xff01;Java 21正式发布了&#xff0c;迎来了史诗级新特性&#xff0c;堪称版本最强&#xff01;&#xff01;&#xff01; 视频链接&#xff1a;https://www.bilibili.com/video/BV1E8…

基于Java的自助多张图片合成拼接实战

目录 前言 一、图片合成需求描述 二、图片合成设计与实现 1、编程语言 2、基础数据准备 3、图片合成流程 4、图片合成实现 三、总结 前言 在当今数字化时代&#xff0c;图像处理技术在各个领域都发挥着至关重要的作用。从社交媒体到电子商务&#xff0c;从在线教育到虚拟…

计算机网络结课设计:通过思科Cisco进行中小型校园网搭建

上学期计算机网络课程的结课设计是使用思科模拟器搭建一个中小型校园网&#xff0c;当时花了几天时间查阅相关博客总算是做出来了&#xff0c;在验收后一直没管&#xff0c;在寒假想起来了简单分享一下&#xff0c;希望可以给有需求的小伙伴一些帮助 目录 一、设计要求 二、…

在npm上传属于自己的包

最近在整理代码&#xff0c;上传到npm方便使用&#xff0c;所以学习了如何在npm发布一个包&#xff0c;整理写成一篇文章和大家一起交流。 1、注册npm账号 npm | Home 2、确保是登录状态 &#xff08;在包目录下&#xff0c;终端执行 npm login) 按enter键自动打开页面&…

物联网(IoT)详解

物联网&#xff08;IoT&#xff09;详解 1. IoT定义简介2. IoT工作原理3. IoT关键技术4. 物联网与互联网区别5. IoT使用场景6. 开源物联网平台7. 参考资料 1. IoT定义简介 首先第一个问题&#xff0c;什么是物联网&#xff08;IoT&#xff09;? 物联网&#xff08;英文&#…

idea项目列表不出现,展示loading

2025年02月08 11:23:36 星期六 发生在webstorm中&#xff0c;跟其他idea类似 原因是将 ignore 插件升级到 4.5.5 版本 https://github.com/JetBrains/idea-gitignore/pull/933 解决方案&#xff1a;将ignore版本将为 4.5.4 我是将 4.5.5 降低为 4.5.4 正常显示文件夹了。

【Vue】在Vue3中使用Echarts的示例 两种方法

文章目录 方法一template渲染部分js部分方法一实现效果 方法二template部分js or ts部分方法二实现效果 贴个地址~ Apache ECharts官网地址 Apache ECharts示例地址 官网有的时候示例显示不出来&#xff0c;属于正常现象&#xff0c;多进几次就行 开始使用前&#xff0c;记得先…

Ollama 简单 好用 好玩

简介 Ollama https://github.com/ollama/ollama/ 是一个基于 Go 语言 的 本地大语言模型运行框架&#xff0c;专注于本地化运行大型语言模型&#xff08;LLM&#xff09;的开源工具。 类 Docker 产品&#xff08;支持 list,pull,push,run 等命令&#xff09;&#xff0c;更好玩…

储能系统-系统架构

已更新系列文章包括104、61850、modbus 、单片机等&#xff0c;欢迎关注 IEC61850实现方案和测试-1-CSDN博客 快速了解104协议-CSDN博客 104调试工具2_104协议调试工具-CSDN博客 1 电池储能系统&#xff08;BESS&#xff09; 架构 电池储能系统主要包括、电池、pcs、本地控制…

百度高德地图坐标转换

百度地图和高德地图的侧重点不太一样。同样一个地名&#xff0c;在百度地图网站上搜索到的地点可能是商业网点&#xff0c;在高德地图网站上搜索到的地点可能是自然行政地点。 高德地图api 在高德地图中&#xff0c;搜索地名&#xff0c;如“乱石头川”&#xff0c;该地名会出…

网络安全溯源 思路 网络安全原理

网络安全背景 网络就是实现不同主机之间的通讯。网络出现之初利用TCP/IP协议簇的相关协议概念&#xff0c;已经满足了互连两台主机之间可以进行通讯的目的&#xff0c;虽然看似简简单单几句话&#xff0c;就描述了网络概念与网络出现的目的&#xff0c;但是为了真正实现两台主机…

QTreeView和QTableView单元格添加超链接

QTreeView和QTableView单元格添加超链接的方法类似,本文仅以QTreeView为例。 在QTableView仿Excel表头排序和筛选中已经实现了超链接的添加,但是需要借助delegate,这里介绍一种更简单的方式,无需借助delegate。 一.效果 二.实现 QHTreeView.h #ifndef QHTREEVIEW_H #def…

2025年日祭

本文将同步发表于洛谷&#xff08;暂无法访问&#xff09;、CSDN 与 Github 个人博客&#xff08;暂未发布&#xff09; 本蒟自2025.2.8开始半停课。 以下是题目格式&#xff1a; [题目OJ 题号] [来源&#xff08;选填&#xff09;] 名称 …… 题号 - 名称 题目&#xff1a;……

迅为RK3568开发板篇OpenHarmony实操HDF驱动配置LED-编译源码

重新编译 Openharmony4.1 源码&#xff0c;如下所示&#xff1a; ./build.sh --product-name rk3568 --ccache 或者单独编译部件 ./build.sh --product-name rk3568 --build-target demos --ccache 编译之后&#xff0c;在源码 out/rk3568/topeet 目录下生成编译产物&#xff0…

跨越边界,大模型如何助推科技与社会的完美结合?

点击蓝字 关注我们 AI TIME欢迎每一位AI爱好者的加入&#xff01; 概述 2024年&#xff0c;大模型技术已成为人工智能领域的焦点。这不仅仅是一项技术进步&#xff0c;更是一次可能深刻影响社会发展方方面面的变革。大模型的交叉能否推动技术与社会的真正融合&#xff1f;2025年…

数据仓库和商务智能:洞察数据,驱动决策

在数据管理的众多领域中&#xff0c;数据仓库和商务智能&#xff08;BI&#xff09;是将数据转化为洞察力、支持决策制定的关键环节。它们通过整合、存储和分析数据&#xff0c;帮助组织更好地理解业务运营&#xff0c;预测市场趋势&#xff0c;从而制定出更明智的战略。今天&a…

C++---命名空间

目录 c语言中的问题命名空间的定义注意事项第一点&#xff1a;同名命名空间第二点&#xff1a;命名空间中的全局变量与局部变量 命名空间的使用第一种使用方法第二种使用方法第三种使用方法 注意事项第一点&#xff1a;没有名字的命名空间第二点&#xff1a;局部优先原则第三点…