真正去理解Qt5的多线程:手把手创建一个多线程控制台程序(使用movetothread方法)后附百度网盘下载地址

【虚拟化生态平台】虚拟化平台esxi挂载USB硬盘 虚拟化平台安装好之后,一般情况下,只有一个硬盘在工作,其他的外接硬盘(我的只有一个ssd硬盘,其他的均是usb外接,SATA接口的硬盘是否这样我暂时不清楚哈)没有在工作,但是一个硬盘明显不够我们使用的,所以我们需要去挂载外接的硬盘来扩容。... 阅读详情

了很多网上关于Qt多线程的应用实例文章,没有几篇真正讲明白Qt多线程原理的。后来发现知乎上有个叫“万丈高楼平地起”的作者写的两篇:
《Qt 多线程编程之敲开 QThread 类的大门》 https://zhuanlan.zhihu.com/p/53270619
《Qt 中的多线程技术》 https://zhuanlan.zhihu.com/p/52612180
再加上官方的说明文档和例程,基本搞清楚了,在官方例程的基础上进行了修改,也供各位Qt学习者参考。本文也参考了以上两篇文章的部分内容。针对官方推荐的第二中方法(moveToThread)实现多线程。

重点概念

先讲几个重点。详细理论请参考以上两篇文章:

(1) QThread类继承于QObject。一个QThread实例管理程序中的一个线程。QThread的执行开始于run()。run函数默认调用exec()实现事件循环。将自己的代码实现放到run函数中会让run函数变得臃肿庞大,后续难以维护,也不利于面向对象开发。采用重写run函数时,如果忘了在 run() 函数中调用 exec(),则没有事件循环了,执行完run就结束线程了。
(2)QObject 中的 moveToThread() 函数可以在不破坏类结构的前提下依然可以在新线程中运行。你可以使用QObject::moveToThread()将自定义的类对象(Worker类)移动到线程容器(即QThread类的一个实例)中使用。通过线程容器中的方法以及Qt的信号-槽机制实现对Worker类的访问和控制。

在这里插入图片描述

(3)controller、worker 对象到底在哪个线程?「在哪创建就属于哪」这句话放在任何地方都是适用的。而 moveToThread() 函数的作用是将槽函数在指定的线程中被调用。也就是说controller、worker对象均在主线程中。除了与controller绑定的槽函数(包括槽函数体里调用的函数)外,worker的其余函数也在主线程中执行。

实验目的

本次实验中首先在main函数中实现了一个简单的命令交互终端(while循环,用户可以发送命令控制各个线程的启动start、停止stop和检查check。在main函数中创建两个线程容器(controller1和controller2),每个线程中需要做的具体事情由on_doSomething()函数实现。(本次实验中,要做的事就是不停地让变量count累加1)。

实验过程

(1)创建 基于QObject 的子类 Worker,这个类有个成员函数 on_doSomething(),假如该函数中运行的代码非常耗时。因此需要将这个类对象“移动”到新线程里。这里将on_doSomething放到Workerloop函数中的while()中。通过执行startWorker函数,启动循环体。这样“”耗时工作“” 就在新线程中运行了。Workerloop中的循环体会根据成员变量isStop值来判断是否中止。
Worker 类需要有个槽函数用于执行外界的命令,还需要有个信号来向外界发送结果。在主线程里有个 signal 信号来关联并触发startWorker函数(这里没有使用信号-槽机制改变isStop的值,而是直接在主线程中调用closeWorker函数)。与此同时 Worker 类中有个 signal 信号(resultReady)用于向外界发送运行的结果(本次实验中main函数中存在命令交互while(),因此运行过程中无法实时显示,只能在命令交互结束后显示,因此没有使用)。如下列代码:

#ifndef WORKER_H
#define WORKER_H
#include <QObject>

class Worker : public QObject
{
    Q_OBJECT
public:
    explicit Worker( QString name,QObject *parent = nullptr);
signals:
    void resultReady(const QString &str);//向外界发送结果,本实验中没有使用
public slots:
    void startWorker();
    void closeWorker();
private:
    void Workerloop();//事件循环
    void on_doSomething();//耗时操作
private:
    bool isStop;
    int count;
    QString m_name;
};
#endif // WORKER_H

为了体现是在不同线程中执行的,我们在各个函数中打印当前线程 ID。

#include "worker.h"
#include <QDebug>
#include <QThread>

Worker::Worker(QString name,QObject *parent) : QObject(parent)
{
    m_name =  name;
    qDebug()<<"I'm "<< m_name <<" initialized in thread: "<<QThread::currentThreadId();
    isStop=false;
    count = 0;
}
void Worker::startWorker()
{
    qDebug()<<"I'm "<< m_name <<" startWorker in thread: "<<QThread::currentThreadId();
    isStop = false;
    Workerloop();
}
void Worker::closeWorker()
{
     qDebug()<<"I'm "<< m_name <<" closeWorker in thread: "<<QThread::currentThreadId();
    isStop = true;
}
void Worker::Workerloop()
{
     qDebug()<<"I'm "<< m_name <<" Workerloop in thread: "<<QThread::currentThreadId();
    while(!isStop)
    {
        on_doSomething();
        QThread::sleep(1);
    }
}
void Worker::on_doSomething()
{
    count++;
    qDebug()<<"I'm "<< m_name <<" working in thread: "<<QThread::currentThreadId();
 /*   QString workmsg;
    workmsg.sprintf("conut=: %d",count);
    emit resultReady(workmsg);*/
}

(2)创建基于QObject 的Controller类,用来控制线程的开始和停止。在作为“外界”的 Controller 类中可以设计一个类似的启动Worker类的信号,但这里直接使用QThread::started()信号来代替。on_receivResult() 槽函数用于接收新线程的运行结果(没有使用)。Controller的槽对应着Worker的信号。

#ifndef CONTROLLER_H
#define CONTROLLER_H

#include <QObject>
#include <QThread>
#include "worker.h"

class Controller : public QObject
{
    Q_OBJECT
public:
    explicit Controller(int ctrlId,QString workname,QObject *parent = nullptr);
    ~Controller();
    void start();
    void stop();
signals:    

public slots:
    void on_receiveResult(const QString &str);
public:
bool isRunning;
private:
    QThread m_workThread;//线程容器。直接实例化,需要在析构函数中quit()+wait()
    Worker *m_worker;//线程中的对象 new在堆上,需用deleteLater().
    int m_ctrlId;
    QString m_workname;
};
#endif // CONTROLLER_H

QThread 类的实例通过调用 start() 函数发出的started信号就可以启动函数在新线程中运行。
正常的退出线程其实质是退出事件循环,即执行 exit(int returnCode = 0) 函数。返回0代表成功,其他非零值代表异常。quit() 函数等价于 exit(0)。线程退出后会发出 finished() 信号。

#include "controller.h"
#include <QDebug>
Controller::Controller(int ctrlId,QString workname,QObject *parent) : QObject(parent)
{
    m_ctrlId = ctrlId;
    m_workname = workname;
    qDebug()<<"Controller is running in main thread: "<<QThread::currentThreadId();
    isRunning = false;
    m_worker = new Worker(workname);
    m_worker->moveToThread(&m_workThread);

    connect(&m_workThread,&QThread::started,m_worker,&Worker::startWorker);
    connect(&m_workThread,&QThread::finished,m_worker,&QObject::deleteLater);
    connect(m_worker,&Worker::resultReady,this,&Controller::on_receiveResult);
}
Controller::~Controller()
{
	if(m_workThread.isRunning())
    {
    	m_worker->closeWorker();
        m_workThread.quit();
        m_workThread.wait();
     }
}
void Controller::start()
{  
    m_workThread.start();
    isRunning = true;
}
void Controller::stop()
{
    if(m_workThread.isRunning())
    {
        m_worker->closeWorker();
        m_workThread.quit();
        m_workThread.wait();
        isRunning = false;
    }
}
void Controller::on_receiveResult(const QString &str)
{
    qDebug()<< str;
}

执行 Controller::~Controller()析构函数时,m_workThread对象会被销毁。但删除正在运行的QThread(即isFinished()返回false)将导致程序崩溃。因此增加 m_workThread.quit()和 m_workThread.wait();
(3)在main函数中,创建两个Controller类的实例,Id号分别为Thrd0和Thrd1;Controller类中的Worker类的初始化name分别为:“Tom"和"Jerry”。
在main函数中利用while启动一个终端交互,利用QTextStream 监测键盘输入的命令字:
exit 退出终端交互
start1 开启线程1
start2 开启线程2
stop1 退出线程1
stop2 退出线程2
check1 检查线程1是否运行
check2 检查线程2是否运行

#include <QCoreApplication>
#include <QThread>
#include <QDebug>
#include <controller.h>

enum CTRID{
    Thrd0,
    Thrd1
};
int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);
    qDebug()<<"The main threadID is: "<< QThread::currentThreadId();

    QString workername1("Tom");
    QString workername2("Jerry");
    Controller controller1(Thrd0,workername1);
    Controller controller2(Thrd1,workername2);

    qDebug()<<"you can enter the following commands by tapping the keyboard:\n";
    qDebug()<<"\tstart1\n\tstart2\n\tstop1\n\tstop2\n\tcheck1\n\tcheck2\n\teixt\n";

    QTextStream qin(stdin);
    QString cmd;
    while(1)
    {
        qin>>cmd;
        qDebug()<<"I'm running in main: "<<QThread::currentThreadId();
        qDebug()<<"receive:"<<cmd<<endl;

        if(cmd=="exit")
         {
            controller1.stop();
            controller2.stop();
            qDebug()<<"main had exited.you can close this console. "<<endl;
            break;
        }
        if(cmd=="check1")
            qDebug()<<"controller1 status is: "<<controller1.isRunning<<endl;
        if(cmd=="check2")
            qDebug()<<"controller2 status is: "<<controller2.isRunning<<endl;
        if(cmd=="start1")
        {
            controller1.start();
            qDebug()<<"controller1 is started"<<endl;
        }
        if(cmd=="start2")
        {
            controller2.start();
            qDebug()<<"controller2 is started"<<endl;
        }
        if(controller1.isRunning==true)
        {
            if(cmd=="stop1")
            {
                controller1.stop();
                qDebug()<<"controller1 is stoped"<<endl;
            }
        }
        if(controller2.isRunning==true)
        {
            if(cmd=="stop2")
            {
                controller2.stop();
                qDebug()<<"controller2 is stoped"<<endl;
            }
        }
    }
    return a.exec();
}

看看执行效果:
在这里插入图片描述
启动后,在main函数中完成Controller的实例化和Worker的实例化,因此,Controller1、Controller2、Worker1和Worker2均在存在于主线程0x2524中。
在这里插入图片描述
分别输入“start1”、“start2”命令,可以看出,两个线程中的Worker分别工作在0x20e0和0x1cac两个线程中。
分别输入“stop1”、“stop2”命令,可以中止两个线程。
说明:中止线程后,再次输入start1、start2,虽然线程能启动,但controller无法执行构造函数,Worker无法启动,因此不能输出信息。

源代码已经上传,可免费下载。
https://download.youkuaiyun.com/download/SmartTiger_CSL/12158072
本来这个源代码资源是让大家免费下载的,可是优快云平台强制改成了需要积分才能下载,因此,这里给出百度网盘免费下载地址
链接1:https://pan.baidu.com/s/1TWvURe7syAVBSaQd8EpTMg
提取码:6d7z
在这里插入图片描述
链接2:https://pan.baidu.com/s/1tq5oUv1e09VUCxEg1C4xMQ
提取码:qm4g

堆一呼一吸之间:动态扩缩容引发JVM堆内存震荡的伸缩算法、代际传导与容器定容解析 Kubernetes扩容后,新Java实例的GC频率常比老实例高出数倍,堆容量反复伸缩。本文从一次支付系统的GC陡增出发,结合HotSpot源码与JDK 8实测,拆解堆伸缩的记账规则:期望容量以GC后已用量为基数、以Xms为下限,按空闲比例判断扩缩,收缩只在老年代回收后按0%、10%、40%、100%步进;再剖析年轻代随老年代联动、晋升风暴与元空间触发线,对比Parallel、G1、ZGC等收集器及JDK 26的变化,讨论容器分账、HPA共振与原地扩缩容,最后给出分级定容、预热与监控的方案。 阅读详情

相关推荐

STM32F103RC学习(一)

STM32F103RC学习(一)

Cat_South_North的博客 6057

Qt多线程编程-moveToThread()方法

Qt多线程编程,moveToThread()方法

propor的专栏 673

五段式S型算法笔记

又因为有 v-v0= 0.5jt通式: 所以有 vm-v0= 0.5jtm 所以有4vm-4v0= 2jtm。算法1: v=2vm-v0-0.5*jt+2jt*tm-2jtm*tm。加加速度j 自己设置 已知,vmax自己设置已知;所以有4vm-4v0= 2jtm带入算法1。

qq_36658033的博客 681

Qt中信号和槽的连接方式详解(多线程)

Qt中五种不同的信号和槽连接方式在多线程环境下的应用非常重要。根据不同的需求和场景,选择合适的连接方式能够保证程序的高效运行和数据的安全处理。 1.Qt::AutoConnection 2.Qt::DirectConnection 3.Qt::QueuedConnection 4.Qt::BlockingQueuedConnection 5.Qt::UniqueConnection

weixin_43672168的博客 3613

无线网卡选购指南:哪些型号支持Kali Linux的监听模式?附免驱动网卡推荐

本文详细解析了如何在Kali Linux下选购支持监听模式的无线网卡。核心在于选择兼容的芯片组,如Atheros AR9271和Ralink RT3070等经典型号,并提供了免驱动网卡推荐与实战配置指南,帮助安全研究人员和爱好者快速搭建无线测试环境。

wind6的博客 1555

树莓派与stm32通信超详细解答过程

笔者之前发过关于stm32与openmv通信的教程,在学习了几天opencv后,想实现树莓派4B与stm32进行通信。于是上网查询资料,最后实现了两者之间的通信,相信这对大多数电赛人都很有帮助,opencv的识别效果是远高于openmv以及K210的。在此我参考了以下资料辅助我学习,大家可以参考。基于树莓派4B与STM32的UART串口通信实验(代码开源)_树莓派4b串口波特率-优快云博客。

Halcyon0804的博客 5465

自己动手写QT多线程demo

参考如下文章写的demo,添加可暂停功能、一个线程类创建多个线程方式,并测试sleep等影响。 (300条消息) Qt学习之路---线程启动与关闭_hanzhaoqiao1436的博客-优快云博客_qt线程退出释放 (299条消息) QT创建和关闭线程_红药噻的博客-优快云博客 ...

yantuguiguziPGJ的博客 542

NCBI|叶绿体基因组数据上传

叶绿体基因组NCBI上传 小编最近在NCBI上传了好几次数据…想着可以把具体的上传步骤写下来,供大家参考。以下是叶绿体基因组的上传示例,大家可以瞅瞅 图片 准备工作 1.登录NCBI官网:https://www.ncbi.nlm.nih.gov/, 依次点击“sign in to NCBI”——“See more 3rd party sign in options”——搜索“Microsoft live.com”。 ”sign in to NCBI“ ”See more 3rd party sign i

weifanbio的博客 3900

QT开发:详解 Qt 多线程编程核心类 QThread:基本概念与使用方法

QT开发:详解 Qt 多线程编程核心类 QThread:基本概念与使用方法

编程技术探索者,分享C/C++、C#、Java、数据库等开发经验,聚焦实战技巧与AI兴趣,助力编程爱好者成长。 2499

Python 实战:高效绕过 reCAPTCHA v3 的自动化策略

本文深入探讨了使用Python高效绕过reCAPTCHA v3验证的实战策略。文章首先解析了reCAPTCHA v3无感验证的评分机制,随后详细介绍了两种核心解决方案:一是利用CapSolver等专业API服务直接获取有效token,二是通过Playwright模拟真人浏览器行为以提升评分。文中提供了从定位关键参数到代码实现的完整流程,并强调了合规操作与请求频率控制的重要性,为自动化开发者提供了切实可行的技术指南。

weixin_29057065的博客 489

YOLOv12涨点改进 | 独家创新-注意力改进篇 | ECCV 2024 | YOLOv12引入AgentAttention代理注意力模块,兼顾 Softmax 和线性注意力优点,提高检测精度

🔥本文给大家介绍使用Agent Attention模块改进YOLOv12网络模型。其主要作用是通过引入高效的代理token来减少计算复杂度,同时保留全局上下文建模能力。相比传统的Softmax注意力,Agent Attention通过代理token聚合并广播信息,从而降低了模型在处理高分辨率图像时的计算负担,并显著提高了推理速度。其优势在于保持了目标检测中的全局信息处理能力,同时加速了生成过程,特别是在高分辨率场景下,可以提高检测精度、减少内存消耗,并提升处理速度。

Ai缝合怪、助力小伙伴高效发论文 155

QT学习笔记17(Qt多线程)

一、基础知识 1、多线性的优点: 提高应用程序的相应速度。通常情况下,应用程序都在一个线程中执行,当调用一个耗时操作时,用户界面会冻结。多线程可以解决这个问题。 使得多CPU系统更加有效。当线程不超过CPU数目时,操作系统可以调度不同的系统运行在不同的CPU上。 改善程序结构,一个既长又复杂的进程可以分成多个线程。 2、多线程的特点: 多线程的行为无法预测,当多次执行上述应用程序时,每一...

灰太狼的小秘密 800

【Bug已解决】[Bug]: ValueError: No user query found in messages QWEN 3.5 27B VLLM 0.16.0 NIGHTLY 解决方案

用 vLLM 0.16.0 nightly 给发聊天请求时,服务端在处理消息时抛ValueError,请求失败。

博客 408

Qt学习笔记(四)多线程

在Qt中,多线程的处理一般是通过QThread类来实现。QThread代表一个在应用程序中可以独立控制的线程,也可以和进程中的其他线程共享数据。之前我们在Linux应用篇中提到过多线程的概念,使用它的好处是可以同时操作好几个目标,而不是因为上一个目标未结束使得需要的操作陷入阻塞状态。

sincerelover的博客 898

计算机控制技术及应用

《计算机控制技术及应用》通过总结作者的科研成果和吸收国内外的先进理论、方法和技术,以计算机控制系统的网络化、开放化、智能化和集成化发展趋势为主线索,主要介绍计算机控制系统的基本概念和计算机控制系统的典型形式,计算机控制系统的数学描述与基本分析方法,以及数字PID控制器、最少拍控制系统、达林算法、状态变量设计法等设计方法及其各种改进算法。重点突出计算机控制系统输入输出通道接口的设计方法和典型控制系统的设计案例与实现方法等。

OV9734 DATASHEET,完整规格书

OV9734完整规格书,包括电气参数,尺寸规格,寄存器配置表等

Qt中多线程写法一(步骤讲解+代码+加演示)

1.线程简单介绍 进程 运行中的程序!没运行的不算 main函数开始运行 到 main函数结束 操作系统上资源调度的基本单位! 线程 函数 并发执行 操作系统上程序调度的基本单位! 同一进程内的线程 共享 进程的所有资源! 2.qt多进程怎么写 2.1. 创建类(继承QObject) 2.2 改为继承自QThread 2.3 重写QThread类中的run函数 2.4 信号与槽方式启动QThread对象的start函数 2.5 关联信号与槽,操作 2.6 线程结束 注意点: 线程结束 先 quit

qq_48985554的博客 1万+

visio安装包

Microsoft Office Visio 是一款专业的制图软件。利用经过更新的形状、协作工具和数据链接图表,创建专业级图表以简化复杂的信息。

Qt创建多线程的两种方法

来源:https://github.com/czyt1988/czyBlog/tree/master/tech/QtThread 1.摘要 Qt有两种多线程的方法,其中一种是继承QThread的run函数,另外一种是把一个继承于QObject的类转移到一个Thread里。 Qt4.8之前都是使用继承QThread的run这种方法,但是Qt4.8之后,Qt官方建议使用第二种方法。两种方法区别不大...

ZONGXP的博客 6万+

ssm框架搭建

这个是一个在idea上成功跑起来的ssm框架项目,保存在这里方便以后使用直接下载

Qt多线程编程(一)入门篇

啊啊啊

晴天的博客 2670

moveToThread与析构的问题

在使用QObject::moveToThread(thread)以后,QObject的代码将会在thread线程中运行,但是当停止且删除thread线程后,并不会析构QObject对象,这时需要在创建QObject的对象中删除它,否则可能会存在内存泄露(待验证,但是删除thread后,删除QObject并不会出错)。         另外,不能在删除thread之前删除QObject,因为Q

fujicororo的专栏 1944

QT moveToThread解析

目录简介源码分析判断是否可以执行移动动作执行移动动作调用moveToThread_helper调用setThreadData_helper 简介 每一个QObject子类都会关联到一个具体QThread线程上,QObject有一个QThreadObject数据成员,该成员在Qobject构造时关联到具体的线程上: class Q_CORE_EXPORT QObjectPrivate : public QObjectData { ... QThreadData *threadData; // id of

weixin_42981623的博客 6910

QThread moveToThread的妙用

qt thread的使用示例

Wwc_code的博客 287

深入理解 Qt 的 `moveToThread`:提升多线程应用性能的关键

Qt 的 是一个非常有用的特性,它允许开发者将一个对象或组件移动到一个新的线程中执行。这在多线程编程中非常重要,因为它可以避免阻塞主线程,提高应用的响应性和性能。在现代软件开发中,多线程编程已成为提高应用性能和响应性的关键技术之一。Qt,作为一个跨平台的应用程序框架,提供了强大的多线程支持。其中, 功能是实现线程间通信和任务调度的重要工具。本文将详细介绍 的概念、使用方法以及最佳实践。 是 Qt 中 QObject 类的一个成员函数,允许将一个 QObject 或其派生类的对象移动到指定的线程中。这意味

dont worry about it的博客 2万+

17、Qt线程(二):QObject::moveToThread

Qt使用QObject::moveToThread函数和新建类实现多线程

Viciower的博客 970

QThread、moveToThread用法详述

QThread、moveToThread用法详述

danshiming的博客 4716

C#多线程基础,适合新手了解

一、创建线程   在整个系列文章中,我们主要使用Visual Studio 2015作为线程编程的主要工具。在C#语言中创建、使用线程只需要按以下步骤编写即可: 1、启动Visual Studio 2016,新建一个控制台应用程序。 2、确保该控制台程序使用.NET Framework 4.6或以上版本。然而在该篇中的所有示例使用较低版本可以正常工作。 3、双击打开该控制台应用程序中的...

weixin_30685029的博客 129

Qt之moveToThread

moveToThread 是 Qt 中用于管理对象线程亲和性(Thread Affinity)的核心方法。它的作用是将一个 QObject 派生对象移动到指定的线程中,从而改变该对象的线程上下文。理解并正确使用 moveToThread 是编写多线程 Qt 应用程序的关键。

Mr_Jaychong的博客 2561

moveToThread使用技巧

moveToThread

avrdpy的专栏 4793
上一篇: 树莓派4B打开文件管理器时闪退
下一篇: 利用Qt多线程机制实现双路串口数据流的接收和发送(附工程源代码)
SmartTiger_CSL
博客等级 码龄8年 217粉丝 28原创
评论 2
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值