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

Qt中的多线程

Qt中有多种方法实现多线程:

  1. QThread
  2. QThreadPool和QPunnable(重用线程)
  3. Qt Concurrent
  4. WorkerScript(QML中的线程)

QThread 在上两篇文章中已经解释了,这里就不再赘述。 

QThreadPoo和QRunnable(实现的多线程)

QRunnable:

QRunnable 类是一个接口,用于表示需要执行的任务或代码段,由 run 函数的重新实现表示。

autoDelete()返回是否自动删除
setAutoDelete()设置自动删除(thue)

设置自动删除的话,在QThreadPool中会自动删除QRunnable

 使用的注意事项:

 启用自动删除时,使用相同的 QRunnable 多次调用 QThreadPoool::start() 会产生争用条件,不建议这样做。

QRunnable和QThread的区别:

  

 QThreadPool(线程池)

顾名思义,是有很多线程的池子, 可以管理池子中的线程。

线程池的优点:

  1. 降低资源消耗。 通过重复利用已创建的线程降低线程创建和销毁造成的消耗。
  2. 提高响应速度。 当任务到达时,任务可以不需要等到线程创建就能立即执行。
  3. 提高线程的可管理性。 线程是稀缺资源,使用线程池可以进行统一的分配、调优和监控。
  4.   

常用函数: 

setMaxThreadCount()  

设置线程池中最大的线程数,默认:QThread::idealThreadCount
setExpiryTimeout(int)将超时xx毫秒未使用的线程视为已过期并将退出,已在运行的线程没有影响。建议在创建线程池后立即设置。
setStackSize()       设置线程池工作线程的堆栈大小,当线程池创建新线程时使用, 默认值为 0
clear()清除队列中未开启的可运行项
globalInstance()返回一个全局QThreadPool实例
reserveThread()保留一个线程( 此函数将始终增加活动线程数)
releaseThread()释放reserveThread()保留的线程
start(QRunnable,int)开启一个线程,可以设置优先级
tryStart()尝试开启一个线程
tryTake()尝试从队列中删除指定的尚未启动的可运行对象(谨慎使用)
waitForDone(int)等待所有线程退出最多毫秒毫秒,并从线程池中删除所有线程。如果删除了所有线程返回true
activeThreadCount()当前活跃的线程数

启动QRunnable线程的方法: 

  1. 使用QThreadPool::globalInstance()->start()启动   (全局)
  2. 创建一个QThreadPool 对象,然后通过该对象启动

Runnable类:

#ifndef RUNNABLE_H
#define RUNNABLE_H
#include<QThread>
#include<QDebug>
#include<QRunnable>class Runnable:public QRunnable
{
public:explicit Runnable()//构造函数{}~Runnable()//析构函数{}void run()//重写run函数{qDebug()<<"Runnable中的run函数";qDebug()<<"thread to run():"<<QThread::currentThreadId();}
};
#endif // RUNNABLE_H

1.使用QThreadPool::globalInstance()->start()启动   (全局)

Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget)
{ui->setupUi(this);qDebug()<<"主线程号:"<<QThread::currentThread();Runnable *able=new Runnable;QThreadPool::globalInstance()->start(able);}

  

2.创建一个QThreadPool 对象,然后通过该对象启动

Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget)
{ui->setupUi(this);qDebug()<<"主线程号:"<<QThread::currentThread();Runnable *able=new Runnable;//QThreadPool::globalInstance()->start(able);QThreadPool pool;pool.start(able);}

  

 

线程池函数的使用

    QThreadPool pool;qDebug()<<"本机的线程数"<<QThread::idealThreadCount();qDebug()<<"线程池中的线程数"<<pool.maxThreadCount();pool.setMaxThreadCount(10);//设置线程池中最大的线程数pool.setExpiryTimeout(40000);//如果线程40000毫秒,40秒未使用,使它过期Runnable *runnable=new Runnable;pool.reserveThread();//保留一个线程pool.start(runnable);//开启线程qDebug()<<"当前活跃线程数:"<<pool.activeThreadCount();pool.releaseThread();//释放那个保留的线程qDebug()<<"当前活跃线程数:"<<pool.activeThreadCount();

    

QRunnable释放内存问题:

QRunnable可以执行完释放内存:需要将autoDelete()设置为true,默认为true。

#ifndef RUNNABLE_H
#define RUNNABLE_H
#include<QThread>
#include<QDebug>
#include<QRunnable>class Runnable:public QRunnable
{
public:explicit Runnable()//构造函数{}~Runnable()//析构函数{qDebug()<<__FUNCTION__;}void run()//重写run函数{qDebug()<<"thread to run():"<<QThread::currentThreadId();}
};
#endif // RUNNABLE_H
    QThreadPool pool;Runnable *runnable=new Runnable;pool.start(runnable);//开启线程

执行完后自动调用析构函数。

  

QRunnable的通信问题:

由于QRunnable是单独的一个类,并没有继承QObject,所以没有connect()函数,所以无法于外界进行通信。

解决该问题的方法:

  • 使用QMetaObject::invokeMethod()
  • 使用多继承的方式,同时继承QRunnable和QObject,但这样的话会使接口混乱(尽量不要使用)

QMetaObject::invokeMethod()的创建方法 :

QMetaObject::invokeMethod(QObject *obj, //使用的类const char *member, //调用函数名Qt::ConnectionType type,//调用类型QGenericReturnArgument ret,//用来存储返回值的类型QGenericArgument val0 = QGenericArgument(nullptr),QGenericArgument val1 = QGenericArgument(), QGenericArgument val2 = QGenericArgument(), QGenericArgument val3 = QGenericArgument(),QGenericArgument val4 = QGenericArgument(),QGenericArgument val5 = QGenericArgument(),QGenericArgument val6 = QGenericArgument(), QGenericArgument val7 = QGenericArgument(), QGenericArgument val8 = QGenericArgument(),QGenericArgument val9 = QGenericArgument())//最多有10个参数

*obj:需要把使用类传进去

member:函数的名称

type:调用类型:

  1. Qt::DirectConnnect 立即调用该成员函数
  2. Qt::QueuedConnection 一旦应用程序进入主事件循环,就会发送 QEvent 并调用成员。
  3. Qt::BlockQueuedConnection 当前线程将阻塞,直到事件被传递。使用此连接类型在同一线程中的对象之间进行通信将导致死锁。
  4. Qt::AutoConnection 如果 obj 与调用方位于同一线程中,则同步调用成员;否则,它将异步调用该成员。

val0-val9:最多可以有10个参数

传参的方式:使用宏传参

  1. Q_ARG(type,const Type&value)
  2. Q_RETURN_ARG(type, Type&value)
QString S="hellow";
QMetaObject::invokeMethod(obj,              "Text",Qt::DirectConnection,Q_ARG(QString,"pppp"),Q_RETURN_ARG(QString,S));

Runnable函数:

#ifndef RUNNABLE_H
#define RUNNABLE_H
#include "widget.h"
#include<QThread>
#include<QObject>
#include<QDebug>
#include<QRunnable>
#include<QMetaObject>class Runnable:public QRunnable
{
public:explicit Runnable(QObject *obj1):obj(obj1)//构造函数{}~Runnable()//析构函数{}void run()//重写run函数{  QString S="hellow";QMetaObject::invokeMethod(obj,"Text",Qt::DirectConnection,Q_ARG(QString,"pppp"),Q_RETURN_ARG(QString,S));qDebug()<<"thread to run():"<<QThread::currentThreadId();}QObject *obj=nullptr;//需要传的对象
};
#endif // RUNNABLE_H

主函数:

主类中创建函数开头需要添加:Q_INVOKABLE(这样才直到调用哪一个函数)

#include "widget.h"
#include "ui_widget.h"
#include"runnable.h"
#include<QThreadPool>
class Rnnable;
Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget)
{ui->setupUi(this);QThreadPool pool;Runnable *runnable=new Runnable(this);//传入调用的对象pool.start(runnable);//开启线程
}
Q_INVOKABLE void Widget::Text(QString a,QString b)//创建一个函数,名称要相同
{qDebug()<<a;qDebug()<<b;
}
Widget::~Widget()
{delete ui;
}

    

Qt Concurrent(并发)

QtConcurrent命名空间提供高级 API,可以在不使用互斥锁、读写锁、等待条件或信号量等低级线程原语的情况下编写多线程程序。

使用 QtConcurrent编写的程序会根据可用的处理器内核数量自动调整使用的线程数。这意味着编写的应用程序在部署到多核系统时将自动扩展。

如何使用Qt Concurrent:

1.pro文件中添加

QT +=concurrent

2. 添加头文件

#include<QtConcurrent>

 以下是如何执行QtConcurrent的使用方法:

使用QtConcurrent::run()函数执行内容

 函数模型:

//T为模板类型QFuture<T> QtConCurrent::run(Function)//执行一个函数QFuture<T> QtConCurrent::run(threadpool,Function)//线程池中,取出线程执行function函数

执行一个函数:

void text()
{qDebug()<<"ok";
}
void text1(int a)
{qDebug()<<a;
}
void text2(QString s1,QString s2)
{qDebug()<<s1+s2;
}
int main(int argc, char *argv[])
{QApplication a(argc, argv);QFuture<void> fu=QtConcurrent::run(text);QFuture<void> fu1=QtConcurrent::run(text1,10);QFuture<void> fu2=QtConcurrent::run(text2,QString("aa"),QString("bb")");fu.waitForFinished();//等待完成fu1.waitForFinished();//等待完成fu2.waitForFinished();//等待完成return a.exec();
}

   

通过线程池中的进程执行函数

void text1(int a)
{qDebug()<<a;
}int main(int argc, char *argv[])
{QApplication a(argc, argv);QThreadPool pool;QFuture<void> fu=QtConcurrent::run(&pool,text1,10);fu.waitForFinished();//等待完成return a.exec();
}

  

3.调用常量成员函数:

run()函数可以接收成员函数的指针,第一个参数必须是常量引用或指向类实例的指针。

QByteArray bytearray = "hello world";
QFuture<QList<QByteArray> > future = QtConcurrent::run(bytearray, &QByteArray::split, ',');
QList<QByteArray> result = future.result();

4.调用非常量成员函数:

QImage image = ...;
QFuture<void> future = QtConcurrent::run(&image, &QImage::invertPixels,QImage::InvertRgba);
future.waitForFinished();  


总结:以上三种多线程的特点:

特征QThreadQRunnable和QThreadPoolQtConcurrent::run()

QtConcurrent

(Map,Filter,Reduce)

指定优先级

可运行

事件循环

线程可以通过

信号接收数据更新

可以使用

信号控制螺纹

是(QFutureWatcher)

可以通过

QFuture监控线程

部分是

内置暂停/

恢复/取消功能

适用场景:

生命周期开发任务解决方案
持续运行在另一个线程中重复执行昂贵的操作,其中线程不需要接收任何信号或事件。直接在QThread::run()的重新实现中编写无限循环。在没有事件循环的情况下启动线程。让线程发出信号以将数据发送回 GUI 线程。
持续运行让一个对象存在于另一个线程中,该线程可以根据请求执行不同的任务和/或可以接收要使用的新数据。子类 一个QObject 以创建工作线程。实例化此工作线程对象和 QThread。将工作线程移动到新线程。通过排队的信号槽连接将命令或数据发送到工作器对象。
一次调用在另一个线程中运行新的线性函数,可以选择在运行期间更新进度。Qt提供不同的解决方案:
  • 将函数放在 QThread::run() 的重新实现中并启动 Qthread。发出信号以更新进度。或
  • 将函数放在 QRunnable::run() 的重新实现中,并将 QRunnable 添加到 QthreadPool 中。写入线程安全变量以更新进度。或
  • 使用 Q他Concurrent() 运行函数。写入线程安全变量以更新进度。
一次调用在另一个线程中运行现有函数并获取其返回值。使用 QtConcurrent::run() 运行函数。让 QFutureWatcher 在函数返回时发出 done() 信号,并调用 QFutureWatcher::result() 来获取函数的返回值。
一次调用使用所有可用核心对容器的所有项执行操作。例如,从图像列表生成缩略图。

使用 Qt Concurrent 的 QtConcurrent::filter() 函数选择容器元素,使用 QtConcurrent::map() 函数对每个元素应用操作。要将输出折叠成单个结果,请改用 QtConcurrent::filteredReduction() 和 QtConcurrent::mappedReduction()。

参考文章:

Thread Support in Qt | Qt 5.15

 Qt线程之QRunnable的使用详解_luoyayun361的博客-CSDN博客_qrunnable 

【QT】继承QRunnable+QThreadPool实现多线程_李春港的博客-CSDN博客_继承qrunnable

Qt并发模块Qt Concurrent的使用_Amnesia Greens的博客-CSDN博客_qtconcurrent 

相关文章:

Qt中的多线程

Qt中有多种方法实现多线程&#xff1a; QThreadQThreadPool和QPunnable&#xff08;重用线程&#xff09;Qt ConcurrentWorkerScript&#xff08;QML中的线程&#xff09;QThread 在上两篇文章中已经解释了&#xff0c;这里就不再赘述。 QThreadPoo和QRunnable&#xff08;实现…...

React-Hooks怎样封装防抖和节流-面试真题

Debounce debounce 原意消除抖动&#xff0c;对于事件触发频繁的场景&#xff0c;只有最后由程序控制的事件是有效的。 防抖函数&#xff0c;我们需要做的是在一件事触发的时候设置一个定时器使事件延迟发生&#xff0c;在定时器期间事件再次触发的话则清除重置定时器&#xff…...

算法训练营 day51 动态规划 打家劫舍系列

算法训练营 day51 动态规划 打家劫舍系列 打家劫舍 198. 打家劫舍 - 力扣&#xff08;LeetCode&#xff09; 你是一个专业的小偷&#xff0c;计划偷窃沿街的房屋。每间房内都藏有一定的现金&#xff0c;影响你偷窃的唯一制约因素就是相邻的房屋装有相互连通的防盗系统&#…...

【蓝桥集训】第六天——递归

作者&#xff1a;指针不指南吗 专栏&#xff1a;Acwing 蓝桥集训每日一题 &#x1f43e;或许会很慢&#xff0c;但是不可以停下来&#x1f43e; 文章目录1.树的遍历2.递归求阶乘3.求斐波那契数列1.树的遍历 一个二叉树&#xff0c;树中每个节点的权值互不相同。 现在给出它的后…...

react源码中的hooks

今天&#xff0c;让我们一起深入探究 React Hook 的实现方法&#xff0c;以便更好的理解它。但是&#xff0c;它的各种神奇特性的不足是&#xff0c;一旦出现问题&#xff0c;调试非常困难&#xff0c;这是由于它的背后是由复杂的堆栈追踪&#xff08;stack trace&#xff09;支…...

038.Solidity入门——25调用其他合约的方法

Solidity 提供了几种方式用于调用其他合约&#xff1a;方法描述直接调用使用 address.call 函数&#xff0c;可以向另一个合约发送消息并返回结果。低级调用使用 address.call 或 address.callcode 函数&#xff0c;可以执行一个外部合约中的代码。与直接调用不同&#xff0c;低…...

Revit项目浏览器的标准设置应用和快速视图样板?

一、Revit项目浏览器的标准设置应用 设计院阶段的BIM应用&#xff0c;主要是Revit出施工图方面&#xff0c;需要涉及到很多标准的制定方面的问题&#xff0c;而且这个标准不仅仅是一个命名标准&#xff0c;还有很多的符合本院的出图标准等等&#xff0c;本期就不做详细讨论&…...

安装MQTT Server遇到报错“cannot verify mosquitto.org‘s certificate”,该如何解决?

MQTT是基于发布/订阅的轻量级即时通讯协议&#xff0c;很适合用于低带宽、不稳定的网络中进行远程传感器和控制设备通讯等操作中。在我们的软件研发中&#xff0c;也经常使用MQTT协议进行消息通信等。今天来和大家分享一些关于在安装MQTT Server中遇到的疑难问题及解决思路。当…...

程序员如何向架构师转型?看完就明白该怎么做了

软件行业技术开发从业人员众多&#xff0c;但具备若干年开发经验的普通的开发人员往往面临个人发展的瓶颈&#xff0c;即如何从普通开发人员转型成高层次的系统架构师和技术管理人员。想成为一名架构师&#xff0c;应当具备全面的知识体系&#xff0c;需要进行系统的学习和实践…...

Flask入门(9):蓝图

目录9.蓝图9.1 概述9.2 蓝图项目结构结构1结构29.3 添加前缀9.4 静态文件9.5 模板9.6 构建 URLs9.蓝图 参考&#xff1a;http://www.pythondoc.com/flask/blueprints.html 9.1 概述 Flask 使用了 蓝图 的概念在一个应用或者跨应用中构建应用组件以及支持通用模式。 蓝图很好…...

跑步戴哪种耳机好,最适合运动跑步的蓝牙耳机

经常跑步使用的耳机&#xff0c;还是要选择佩戴着舒适以及牢固的运动耳机最为合适&#xff0c;在运动当中会遇到耳机掉落或者长时间佩戴耳道感到难受的现象发生&#xff0c;那么什么蓝牙耳机是最适合运动当中佩戴呢&#xff1f;下面这些耳机分享希望能够帮助大家。 1、南卡Run…...

微信小程序实现瀑布流布局

微信小程序实现瀑布流布局1、简单实例&#xff0c;纯图片后台返回图片高度https://blog.csdn.net/qq_45967222/article/details/1190318762、纯图片后台返回图片高度、通过wx.getImageInfo获取在线图片高度、按照奇数偶数来显示https://blog.csdn.net/baidu_35290582/article/d…...

2023最新网络工程师HCIA-Datacom“1000”道题库,光速刷题拿证

HCIA认证是华为认证体系的初级认证&#xff0c;可以说是网工进入IT行业的一张从业资格证&#xff01; HCIA-Datacom考试覆盖数通基础知识 包括 TCP/IP 协议栈基础知识&#xff0c;OSPF 路由协议基本原理以及在华为路由器中的配置实现&#xff0c;以太网技术、生成树、VLAN 原…...

[蓝桥杯] 递归与递推习题训练

文章目录 一、递归实现指数型枚举 1、1 题目描述 1、2 题解关键思路与解答 二、递归实现排列型枚举 2、1 题目描述 2、2 题解关键思路与解答 三、递归实现组合型枚举 3、1 题目描述 3、2 题解关键思路与解答 四、带分数 4、1 题目描述 4、2 题解关键思路与解答 五、费解的开关…...

领航智能汽车信息安全新征程 | 云驰未来乔迁新址

2月20日&#xff0c;在北京朝阳百子湾东朝时代创意园&#xff0c;云驰未来迎来乔迁之喜&#xff0c;智能汽车和自动驾驶领域的行业领导、合作伙伴与客户、投资人及媒体嘉宾齐聚现场&#xff0c;共同见证云驰未来迈上新的发展征程。 作为中国智能网联汽车和自动驾驶信息安全行业…...

Kaldi语音识别技术(七) ----- 训练GMM

Kaldi语音识别技术(七) ----- GMM 文章目录Kaldi语音识别技术(七) ----- GMM训练GMMtrain_mono.sh 用于训练GMM训练GMM—生成文件训练GMM—final模型查看训练GMM—final.occs查看训练GMM—对齐信息查看训练GMM—fsts.*.gz查看训练GMM—tree决策树查看align_si.sh 用于对齐训练G…...

Java 集合基础

文章目录一、集合概念二、ArrayList1. 构造方法和添加方法2. 常用方法三、案例演示1. 存储字符串并遍历2. 存储学生对象并遍历3. 键盘录入学生对象并遍历一、集合概念 编程的时候如果要存储多个数据&#xff0c;使用长度固定的数组存储格式&#xff0c;不一定满足我们的需要&a…...

Day896.MySql的kill命令 -MySQL实战

MySql的kill命令 Hi&#xff0c;我是阿昌&#xff0c;今天学习记录的是关于MySql的kill命令的内容。 在 MySQL 中有两个 kill 命令&#xff1a; 一个是 kill query 线程 id&#xff0c;表示终止这个线程中正在执行的语句&#xff1b;一个是 kill connection 线程 id&#…...

L2-010 排座位

布置宴席最微妙的事情&#xff0c;就是给前来参宴的各位宾客安排座位。无论如何&#xff0c;总不能把两个死对头排到同一张宴会桌旁&#xff01;这个艰巨任务现在就交给你&#xff0c;对任何一对客人&#xff0c;请编写程序告诉主人他们是否能被安排同席。 输入格式&#xff1…...

C++的完美讲解,还不快来看看?

目录 简介&#xff1a; 创建C程序&#xff1a; Windows编译简介&#xff1a; Hello,C World! 简介&#xff1a; C融合了3中不同的编程传统:C语言代表的过程性传统、C在C语言基础上添加的类代表的面向对象语言的传统以及C模板支持的通用编程传统。一般来说&#xff0c;计算机语言…...

条件运算符

C中的三目运算符&#xff08;也称条件运算符&#xff0c;英文&#xff1a;ternary operator&#xff09;是一种简洁的条件选择语句&#xff0c;语法如下&#xff1a; 条件表达式 ? 表达式1 : 表达式2• 如果“条件表达式”为true&#xff0c;则整个表达式的结果为“表达式1”…...

【磁盘】每天掌握一个Linux命令 - iostat

目录 【磁盘】每天掌握一个Linux命令 - iostat工具概述安装方式核心功能基础用法进阶操作实战案例面试题场景生产场景 注意事项 【磁盘】每天掌握一个Linux命令 - iostat 工具概述 iostat&#xff08;I/O Statistics&#xff09;是Linux系统下用于监视系统输入输出设备和CPU使…...

鸿蒙中用HarmonyOS SDK应用服务 HarmonyOS5开发一个医院查看报告小程序

一、开发环境准备 ​​工具安装​​&#xff1a; 下载安装DevEco Studio 4.0&#xff08;支持HarmonyOS 5&#xff09;配置HarmonyOS SDK 5.0确保Node.js版本≥14 ​​项目初始化​​&#xff1a; ohpm init harmony/hospital-report-app 二、核心功能模块实现 1. 报告列表…...

ElasticSearch搜索引擎之倒排索引及其底层算法

文章目录 一、搜索引擎1、什么是搜索引擎?2、搜索引擎的分类3、常用的搜索引擎4、搜索引擎的特点二、倒排索引1、简介2、为什么倒排索引不用B+树1.创建时间长,文件大。2.其次,树深,IO次数可怕。3.索引可能会失效。4.精准度差。三. 倒排索引四、算法1、Term Index的算法2、 …...

springboot整合VUE之在线教育管理系统简介

可以学习到的技能 学会常用技术栈的使用 独立开发项目 学会前端的开发流程 学会后端的开发流程 学会数据库的设计 学会前后端接口调用方式 学会多模块之间的关联 学会数据的处理 适用人群 在校学生&#xff0c;小白用户&#xff0c;想学习知识的 有点基础&#xff0c;想要通过项…...

【无标题】路径问题的革命性重构:基于二维拓扑收缩色动力学模型的零点隧穿理论

路径问题的革命性重构&#xff1a;基于二维拓扑收缩色动力学模型的零点隧穿理论 一、传统路径模型的根本缺陷 在经典正方形路径问题中&#xff08;图1&#xff09;&#xff1a; mermaid graph LR A((A)) --- B((B)) B --- C((C)) C --- D((D)) D --- A A -.- C[无直接路径] B -…...

GitHub 趋势日报 (2025年06月06日)

&#x1f4ca; 由 TrendForge 系统生成 | &#x1f310; https://trendforge.devlive.org/ &#x1f310; 本日报中的项目描述已自动翻译为中文 &#x1f4c8; 今日获星趋势图 今日获星趋势图 590 cognee 551 onlook 399 project-based-learning 348 build-your-own-x 320 ne…...

TSN交换机正在重构工业网络,PROFINET和EtherCAT会被取代吗?

在工业自动化持续演进的今天&#xff0c;通信网络的角色正变得愈发关键。 2025年6月6日&#xff0c;为期三天的华南国际工业博览会在深圳国际会展中心&#xff08;宝安&#xff09;圆满落幕。作为国内工业通信领域的技术型企业&#xff0c;光路科技&#xff08;Fiberroad&…...

在 Spring Boot 中使用 JSP

jsp&#xff1f; 好多年没用了。重新整一下 还费了点时间&#xff0c;记录一下。 项目结构&#xff1a; pom: <?xml version"1.0" encoding"UTF-8"?> <project xmlns"http://maven.apache.org/POM/4.0.0" xmlns:xsi"http://ww…...

HTML前端开发:JavaScript 获取元素方法详解

作为前端开发者&#xff0c;高效获取 DOM 元素是必备技能。以下是 JS 中核心的获取元素方法&#xff0c;分为两大系列&#xff1a; 一、getElementBy... 系列 传统方法&#xff0c;直接通过 DOM 接口访问&#xff0c;返回动态集合&#xff08;元素变化会实时更新&#xff09;。…...