当前位置: 首页 > article >正文

解决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里的槽函数进行绑定

相关文章:

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

今天在Qt编程时&#xff0c;将QTimer在子线程里执行start()函数&#xff0c;遇到“Timers cannot be started from another thread”问题&#xff0c;使用了如下AI工具&#xff0c;进行查询&#xff1a;     提示词A&#xff1a;“C QTimer 如何跨线程”     提示词B&#…...

vant4 van-list组件的使用

<van-listv-if"joblist && joblist.length > 0"v-model:loading"loading":finished"finished":immediate-check"false"finished-text"没有更多了"load"onLoad">// 加载 const loading ref(fals…...

C++11新特性之weak_ptr智能指针

本节介绍最后一个智能指针——weak_ptr智能指针。 1.介绍 weak_ptr智能指针也是以模板类的方式实现的。同样定义在<memory>头文件&#xff0c;并位于std命名空间中。在使用前需包含这两条语句。 C11虽然将weak_ptr当做智能指针&#xff0c;但该类型通常不单独使用&#…...

LM Studio无设置代理,更改镜像源方法(MAC)

在macbook上使用LM Studio时发现总是加载失败&#xff0c;App也没有设置代理的地方&#xff0c;搜索了挺多解决方案&#xff0c;貌似官网再可以封补很多解决方案已经过时&#xff0c;最终找到一种替换镜像源的方法共享出来。 方便大家都能使用&#xff0c;不介绍命令行修改方式…...

js中的== 和 ===运算符的比较和区别(面试题)

和 运算符用于比较 JavaScript 值是否相等。 自动转换数据类型&#xff0c;允许不同类型值的比较。 进行严格相等比较&#xff0c;仅在值和数据类型都相同的情况下返回 true。NaN 仅在 比较中与自身相等&#xff0c;而在 比较中不相等。null 和 undefined 仅在 比较中相等。…...

通过客户端Chatbox或OpenwebUI访问识别不到本地ollama中的模型等问题的解决

Chatbox和Open WebUI 等无法获取到 Ollama里的模型&#xff0c;主要是由以下原因导致&#xff1a; Ollama 服务未正确暴露给 Docker 容器或客户端模型未正确下载或名称不匹配网络配置或权限问题 排查以上问题的思路首先排查ollama服务是否启动&#xff0c;然后再看端口号 使…...

C# 上位机--变量

C# 上位机--变量 在 C# 上位机开发领域&#xff0c;变量是构建程序逻辑的基础元素之一。它就像是一个容器&#xff0c;用于存储各种类型的数据&#xff0c;从简单的数值到复杂的对象。正确理解和使用变量&#xff0c;对于开发出高效、稳定且易于维护的上位机程序至关重要。本文…...

【Mastering Vim 2_01】开篇词:在 AI 时代持续深耕底层技术,做长期主义的坚定捍卫者

【最新版《Mastering Vim》封面&#xff0c;涵盖 Vim 9.0 版特性】 文章目录 1 背景&#xff1a;AI 时代的底层技术觉醒2 Vim&#xff1a;一款被严重低估的文本编辑神器3 聊聊 IT 人士的职业病4 进阶之道&#xff1a;构建完整的知识体系5 从 AI 时代的深耕与精进再谈长期主义 1…...

【JVM详解二】常量池

一、常量池概述 JVM的常量池主要有以下几种&#xff1a; class文件常量池运行时常量池字符串常量池基本类型包装类常量池 它们相互之间关系大致如下图所示&#xff1a; 每个 class 的字节码文件中都有一个常量池&#xff0c;里面是编译后即知的该 class 会用到的字面量与符号引…...

Leetcode - 149双周赛

目录 一、3438. 找到字符串中合法的相邻数字二、3439. 重新安排会议得到最多空余时间 I三、3440. 重新安排会议得到最多空余时间 II四、3441. 变成好标题的最少代价 一、3438. 找到字符串中合法的相邻数字 题目链接 本题有两个条件&#xff1a; 相邻数字互不相同两个数字的的…...

Java爬虫:打造高效的数据抓取利器——详解详情接口设计与实现

在当今数字化时代&#xff0c;数据如同黄金般珍贵。无论是企业进行市场调研、竞争对手分析&#xff0c;还是研究人员收集信息&#xff0c;数据的需求无处不在。而爬虫技术&#xff0c;作为一种高效的数据抓取手段&#xff0c;成为了众多开发者手中的利器。本文将深入探讨如何使…...

蓝桥杯K倍区间(前缀和与差分,取模化简)

输入 5 2 1 2 3 4 5 输出 6 思路&#xff1a;首先由连续子串和可以想用前缀和&#xff0c;由于加减法总和取模和分别取模结果不受影响&#xff0c;所以我们前缀和之后直接取模方便观察性质&#xff0c;本题前缀和&#xff1a;1&#xff0c;3&#xff0c;6&#xff0c;10&#…...

CEF132 编译指南 MacOS 篇 - depot_tools 安装与配置 (四)

1. 引言 在 CEF132&#xff08;Chromium Embedded Framework&#xff09;的编译过程中&#xff0c;depot_tools 扮演着举足轻重的角色。这套由 Chromium 项目精心打造的脚本和工具集&#xff0c;专门用于获取、管理和更新 Chromium 及其相关项目&#xff08;包括 CEF&#xff…...

Ubuntu 20.04 上安装 qBittorrent

qBittorrent 通过终端安装 系统更新系统升级在 Ubuntu 20.04 上添加 Qbittorent PPA系统更新Qbittorent 安装 Qbittorent 是一个开源且可免费使用的点对点比特流客户端。它体积小&#xff0c;不加载内存盘。众所周知&#xff0c;此应用程序可以在许多操作系统&#xff08;例如…...

iPhone 在华销量大幅下挫

iPhone在乔布斯时代缔造的神话在中国正逐渐走向没落&#xff0c;挤牙膏式的升级方式类似于诺基亚的N70系列&#xff0c;毫无新意的创新能力&#xff0c;求稳着陆的经营理念&#xff0c;工艺和美学不再独领风骚&#xff0c;甚至拍照领域和AI增强计算&#xff0c;折叠屏等技术领域…...

【Ubuntu VScode Remote SSH 问题解决】Resolver error: Error: XHR failed

1. 问题描述 VScode使用remote ssh 远程服务器&#xff0c;报错类似&#xff1a; [12:06:01.219] Downloading VS Code server locally... [12:06:01.310] Resolver error: Error: XHR failedat k.onerror (vscode-file://vscode-app/private/var/folders/g1/cvs2rnpx60qc3b4…...

在JVM的栈(虚拟机栈)中,除了栈帧(Stack Frame)还有什么?

在JVM的栈&#xff08;虚拟机栈&#xff09;中&#xff0c;除了栈帧&#xff08;Stack Frame&#xff09;&#xff0c;还有其他一些与方法调用相关的重要信息。栈的主要作用是存储方法调用的执行过程中的上下文信息&#xff0c;栈帧是其中最关键的组成部分。 栈的组成 栈帧&am…...

docker发布自己的镜像

官方node-red镜像&#xff1a; nodered/node-red - Docker Image 拉取v3版本&#xff1a; docker pull nodered/node-red:3.1.14 运行镜像&#xff1a; docker run --restartalways --privilegedtrue -d -p 1880:1880 -v node_red_data:/data --name mynodered nodered/n…...

【人工智能】解码语言之谜:使用Python构建神经机器翻译系统

《Python OpenCV从菜鸟到高手》带你进入图像处理与计算机视觉的大门! 解锁Python编程的无限可能:《奇妙的Python》带你漫游代码世界 神经机器翻译(NMT)是近年来机器翻译领域的一项重大突破。它利用深度学习模型,特别是循环神经网络(RNN)和Transformer网络,以端到端的…...

JavaScript数组类型详解

目录 一、数组的基本概念 二、数组的类型 1. 基本数组类型&#xff1a; 2. 数字数组&#xff1a; 3. 字符串数组&#xff1a; 4. 对象数组&#xff1a; 5. 类型数组&#xff08;TypedArray&#xff09;&#xff1a; 6. ArrayBuffer数组&#xff1a; 7. 类数组&#xff…...

【实战AI】利用deepseek 在mac本地部署搭建个人知识库

之前的文章中实现了本地通过ollma 部署deepseek R1&#xff1a;14b 模型&#xff0c;这里我想继续实现个人知识库&#xff0c;方便自己文档&#xff0c;数据的检索&#xff1b; 下载anythingLLM 地址&#xff1a; https://anythingllm.com/desktop 下载安装即可&#xff1b…...

Spring Boot 3.4 中 MockMvcTester 的新特性解析

引言 在 Spring Boot 3.4 版本中&#xff0c;引入了一个全新的 MockMvcTester 类&#xff0c;使 MockMvc 测试可以直接支持 AssertJ 断言。本文将深入探讨这一新特性&#xff0c;分析它如何优化 MockMvc 测试并提升测试的可读性。 Spring MVC 示例 为了演示 MockMvcTester 的…...

Express 中间件

在构建 Web 应用程序时&#xff0c;中间件&#xff08;Middleware&#xff09;扮演着至关重要的角色。它允许你定义一系列的函数来处理 HTTP 请求和响应过程中的各种任务。Express.js 是 Node.js 上最流行的框架之一&#xff0c;以其简洁且强大的中间件机制著称。本文将深入探讨…...

PyCharm结合DeepSeek-R1

PyCharm结合DeepSeek-R1&#xff0c;打造专属 AI 编程助手 在程序员的日常工作中&#xff0c;提高编程效率、快速解决代码问题是重中之重。今天给大家分享一个强强联合的组合 ——PyCharm 插件 Continue 与 DeepSeek-R1&#xff0c;它们能帮你打造出强大的个人 AI 编程助手。 …...

AJAX XML技术详解

AJAX XML技术详解 引言 随着互联网技术的不断发展,前端与后端之间的交互需求日益增长。AJAX(Asynchronous JavaScript and XML)技术应运而生,成为实现前后端分离、提高页面响应速度的关键技术之一。本文将详细介绍AJAX XML技术,包括其原理、应用场景、优缺点等内容。 A…...

【openresty服务器】:源码编译openresty支持ssl,增加service系统服务,开机启动,自己本地签名证书,配置https访问

1&#xff0c;openresty 源码安装&#xff0c;带ssl模块 https://openresty.org/cn/download.html &#xff08;1&#xff09;PCRE库 PCRE库支持正则表达式。如果我们在配置文件nginx.conf中使用了正则表达式&#xff0c;那么在编译Nginx时就必须把PCRE库编译进Nginx&#xf…...

Java+vue前后端分离项目集群部署

一、项目概述 假设我们有一个前后端分离的项目&#xff0c;前端使用React或Vue框架&#xff0c;后端使用Spring Boot或Node.js。我们将分别部署前端和后端到集群环境中。 二、准备工作 1. 代码准备&#xff1a;确保前端和后端代码已经开发完成&#xff0c;并通过本地测试。 2…...

3. CSS中@scope

说说你对 CSS 中scope 的了解 <style>/* scope规则 */scope (#app) {.box {width: 100px;height: 100px;background-color: red;}} </style> <div id"app"><div class"box"></div> </div>CSS 中的scope 是一个相对较新…...

互联网大厂面试高频题-操作系统部分

前言 哈喽各位小伙伴们,本期小梁给大家带来了互联网大厂面试中操作系统部分的高频题,本文会以通俗易懂的语言以及图解形式描述,希望能给大家的面试带来一点帮助,祝大家offer拿到手软!!! 话不多说,我们立刻进入本期正题! 1 说说什么是操作系统吧。 答…...

Sentinel——Spring Boot 应用接入 Sentinel 后内存开销增长计算方式

接入 Sentinel 对 Spring Boot 应用的内存消耗影响主要取决于 规则数量、资源数量、监控粒度、并发量 等因素。 1. 核心内存消耗来源 (1) Sentinel 核心库 默认依赖&#xff1a;Sentinel Core 本身占用较小&#xff0c;通常在 10~50MB&#xff08;取决于资源数量和规则复杂度…...