小编正在学习嵌入式软件,目前建立了一个交流群,可以留下你的评论,我拉你进群

一、简介

队列是为了任务与任务、任务与中断之间的通信而准备的,可以在任务与任务、任务与中断之间消息传递,队列中可以存储有限的、大小固定的数据项目。

通常队列采用先进先出的存储缓冲机制(FIFO)
往队列中发送数据称为入队,从队列中读取数据称为出队
FreeRTOS中队列是通过值传递(默认)进行存储的,如果大数据的时候,也可以通过数据引用(只传递数据的指针)进行存储
队列不属于某个任务,任何任务与中断都可以向队列读取/发送数据

注意:在出队,入队时候代码是进入临界区的,也就是说,在向队列读写数据时不可被比FreeRTOS可管理的最高中断优先级低的中断所打断

出队阻塞:当一个任务要从一个队列中读数据时,但这个队列是空的,这时任务该怎么办呢?一是立刻返回任务继续执行接下来的代码,二是等一段时间后再返回任务,三是一直等待,直到队列中有数据,读到数据再返回任务;这三种方式取决于用户设置的阻塞时间,如果阻塞时间为0就是第一种情况,若是阻塞时间为portMAX_DELAY就是第三种情况,若阻塞时间在0~portMAX_DELAY就是第二种情况

当阻塞时,将该任务的状态列表项挂载到pxDelayTaskList;将该任务的事件列表项挂载到List_t xTasksWaitingToReceive;


入队阻塞:当任务向队列中发数据,而此时队列是满的,没有多余的空间给了,这时任务该怎么办?这与出对遇到的情况类似,程序可以根据有用设定的阻塞时间进行等待
当阻塞时,将该任务的状态列表项挂载到pxDelayTaskList;将该任务的事件列表项挂载到List_t xTasksWaitingToSend; 

当多个任务往一个已经满了的队列中写入时,任务均会进入阻塞状态,当队列有空间时,哪个任务先往队列中写入呢?
这里遵循两个原则:1、优先级最高的任务先写入 ;2、若优先级相同,等待时间最长的任务写入

二、队列结构体 /API

2.1 结构体

typedef struct QueueDefinition
{
int8_t * pcHead;                                  /*存储区域的起始地址,也就是队列项的起始地址 */
int8_t * pcWriteTo;                              /*下一个队列项写入的位置 */

    union
{
QueuePointers_t xQueue;             /* 该结构体用于队列的选项*/
SemaphoreData_t xSemaphore;   /*该结构体用于互斥信号量/递归信号量的选项*/
} u;

    List_t xTasksWaitingToSend;                            /* 等待发送列表 */
List_t xTasksWaitingToReceive;                      /*等待接收列表 */

    volatile UBaseType_t uxMessagesWaiting;    /*非空闲队列项目的数量,队列中已使用的*/
UBaseType_t uxLength;                                  /*队列长度 */
UBaseType_t uxItemSize;                               /*对列项目的大小*/

    volatile int8_t cRxLock;                     /*读取上锁计数器,当使用时操作不了等待接收列表*/
volatile int8_t cTxLock;                    /*写入上锁计数器,当使用时操作不了等待发送列表 */

     /*其他的一些条件编译*/
} xQUEUE;

2.2 API 

创建队列API:

使用队列的主要流程:创建队列->写入队列->读取队列

函数描述
xQueueCreate()动态方式创建队列
(队列所需内存空间由FreeRTOS从FreeRTOS管理的堆中分配)
xQueueCreateStatic()静态方式创建队列
(需要用户自行分配内存)
#define xQueueCreate( uxQueueLength, uxItemSize )    xQueueGenericCreate( ( uxQueueLength ), ( uxItemSize ), ( queueQUEUE_TYPE_BASE ) )

创建队列API函数xQueueCreate()相当于API函数xQueueGenericCreate(),但后者多了一个参数queueQUEUE_TYPE_BASE

/* For internal use only.  These definitions *must* match those in queue.c. */
#define queueQUEUE_TYPE_BASE                  ( ( uint8_t ) 0U ) //队列
#define queueQUEUE_TYPE_SET                   ( ( uint8_t ) 0U ) //队列集
#define queueQUEUE_TYPE_MUTEX                 ( ( uint8_t ) 1U ) //互斥信号量
#define queueQUEUE_TYPE_COUNTING_SEMAPHORE    ( ( uint8_t ) 2U ) //计数型信号量
#define queueQUEUE_TYPE_BINARY_SEMAPHORE      ( ( uint8_t ) 3U ) //二值信号量
#define queueQUEUE_TYPE_RECURSIVE_MUTEX       ( ( uint8_t ) 4U ) //递归互斥信号量

 创建队列API流程
创建队列:xQueueCreat() ——>实际执行的是xQueueGenericCreat() 
1、计算队列需要多大内存xQueueSizeInBytes = ( size_t ) ( uxQueueLength * uxItemSize );
2、为队列申请内存,申请大小为sizeof( Queue_t ) + xQueueSizeInBytes,前面部分存放结构体成员,后面存放队列项
3、判断内存是否申请成功,成功即计算出队列项存储的首地址pucQueueStorage = ( uint8_t * ) pxNewQueue;
4、调用prvInitialiseNewQueue()初始化新队列pxNewQueue
<1>初始化队列结构体成员变量
<2>调用xQueueGenericReset()复位队列
①初始化其他队列结构体成员变量
②判断要复位的队列是否为新创建的队列
若不是新创建的队列,那就复位它,将列表xTasksWaitingToSend移出
若是新创建的队列,那就初始化xTasksWaitingToSend,xTasksWaitingToReceive这两个列表

入队API:

入队函数

带有覆写功能的函数只适用于队列项为1的队列 

四个任务级入队函数调用的其实是相同的一个函数
BaseType_t xQueueGenericSend( QueueHandle_t xQueue,
const void * const pvItemToQueue,
TickType_t xTicksToWait,
const BaseType_t xCopyPosition )

@xQueue :队列句柄,指明要向哪个队列发送数据,创建队列成功后返回此队列句柄
@pvItemToQueue:指向要发送的消息,发送的过程中将这个消息拷贝到队列中
@xTicksToWait:阻塞时间
@xCopyPosition:写入队列位置,有三种方式
queueSEND_TO_BACK:   写入队列尾部
queueSEND_TO_FRONT: 写入队列头部
queueOVERWRITE:           覆写队列(仅用于队列的队列长度为1)

返回值:
pdTRUE :向队列发送消息成功
errQUEUE_FULL:队列已满,发送消息失败                         

入队流程

出队API 

出队API

BaseType_t xQueueReceive( QueueHandle_t xQueue,
void * const pvBuffer, TickType_t xTicksToWait )
BaseType_t xQueuePeek( QueueHandle_t xQueue,
void * const pvBuffer, TickType_t xTicksToWait )
@xQueue:待读取的队列
@pvBuffer:消息读取缓冲区
@xTicksToWait:阻塞时间
返回值:读取成功与失败

出队流程

三、信号量

3.1 简介

信号量是一种解决同步问题的机制,可以实现对共享资源的有序访问其中,
同步”指的是任务间的同步,即信号量可以使得一个任务等待另一个任务完成某件事情后才继续执行;
“有序访问”指的是对被多任务或中断访问的共享资源(如全局变量)的管理,当一个任务在访问(读取或写入)一个共享资源时,信号量可以防止其他任务或中断在这期间访问(读取或写入)这个共享资源

举一个例子,假设某个停车场有 100 个停车位(共享资源),这个 100 个停车位对所有人(访问共享资源的任务或中断)开放。如果有一个人要在这个停车场停车,那么就需要先判断这个停车场是否还有空车位(判断信号量是否有资源), 如果此时停车场正好有空车位(信号量有资源),那么就可以直接将车开入空车位进行停车(获取信号量成功), 如果此时停车场已经没有空车位了(信号量没有资源),那么这个人可以选择不停车(获取信号量失败),也可以选择等待(任务阻塞)其他人将车开出停车场(释放信号量资源), 让后再将车停入空车位。
在上面的这个例子中,空车位的数量相当于信号量的资源数,获取信号量相当于占用了空车位,而释放信号量就相当于让出了占用的空车位。信号量用于管理共享资源的场景相当于对共享资源上了个锁,只有任务成功获取到了锁的钥匙,才能够访问这个共享资源,访问完共享资源后还得归还钥匙,当然钥匙可以不只一把,即信号量可以有多个资源

队列与信号量的区别

3.2 二值信号量

3.2.1简介

二值信号量实际上就是一个队列长度为 1 的队列,在这种情况下,队列就只有空(0)和满(1)两种情况,二值信号量通常用于互斥访问或任务同步, 与互斥信号量比较类似,但是二值信号量有可能会导致优先级翻转的问题。
互斥访问:相当于有一扇门,两个任务,谁获得钥匙谁进入,只有一个任务可以打开门
互斥信号量:二值信号量存在优先级翻转,互斥信号存在优先级继承

获取释放信号量示意图

释放信号量相当于将该标志位置满(1),获取标志相当于将标志位置空(0)

3.2.2相关API函数

使用二值信号量的过程:创建二值信号量-->释放二值信号量(相当于入队)-->获取二值信号量(相当于出队)

函数描述
xSemaphoreCreateBinary()使用动态方式创建二值信号量,创建成功的返回值为二值信号量的句柄
xSemaphoreCreateBinaryStatic()使用静态方式创建二值信号量
xSemaphoreTake()获取信号量
xSemaphoreTakeFromISR().在中断中获取信号量
xSemaphoreGive()释放信号量
xSemaphoreGiveFromISR()在中断中释放信号量
vSemaphoreDelete()删除信号量

创建二值信号量函数xSemaphoreCreateBinary(void)其实是调用函数xQueueGenericCreate( 1, ( semSEMAPHORE_QUEUE_ITEM_LENGTH ), (queueQUEUE_TYPE_BINARY_SEMAPHORE ) ),
@1:队列长度为 1 
@semSEMAPHORE_QUEUE_ITEM_LENGTH:空
@queueQUEUE_TYPE_BINARY_SEMAPHORE:二值信号量
返回值:
创建成功返回句柄
创建失败

释放信号量函数xSemaphoreGive(SemaphoreHandle_t xSemaphore)其实调用函数
xQueueGenericSend( ( QueueHandle_t ) ( xSemaphore ), NULL, semGIVE_BLOCK_TIME, queueSEND_TO_BACK);
@xSemaphore:句柄
@NULL:要传入的数据 
@semGIVE_BLOCK_TIME:阻塞时间为0,不支持阻塞
@queueSEND_TO_BACK:尾部插入
返回值:释放信号量成功;释放信号量失败

获取信号量函数xSemaphoreTake(xSemaphore,xBlockTime)其实调用函数xQueueSemaphoreTake()来实现的
@xSemaphore:句柄
@xBlockTime:阻塞时间,用户可设置
返回值:获取信号量成功;超时,获取信号量失败

3.3 计数型信号量 

3.3.1简介

计数型信号量相当于队列长度大于1的队列,因此计数型信号量能够容纳多个资源,这在计数信号量被创建的时候确定的

使用场景一,时间计数:在这种场合下,每次事件发生后,在事件处理函数中释放计数型信号量(计数型信号量的资源数加 1),其他等待事件发生的任务获取计数型信号量(计数型信号量的资源数减 1),这么一来等待事件发生的任务就可以在成功获取到计数型信号量之后执行相应的操作。在这种场合下,计数型信号量的资源数一般在创建时设置为 0

使用场景二,资源管理:在这种场合下,计数型信号量的资源数代表着共享资源的可用数量一个任务想要访问共享资源,就必须先获取这个共享资源的计数型信号量,之后在成功获取了计数型信号量之后,才可以对这个共享资源进行访问操作,当然,在使用完共享资源后也要释放这个共享资源的计数型信号量。在这种场合下,计数型信号量的资源数一般在创建时设置为受其管理的共享资源的最大可用数量

3.3.2相关API函数

使用计数型信号量的过程:创建计数型信号量-->释放计数型信号量(相当于入队)-->获取计数型信号量(相当于出队)
计数型信号量的就是一个队列长度为计数型信号量最大资源数的队列,而队列的非空闲项目数量就是用来记录计数型信号量的可用资源的

函数描述
xSemaphoreCreateCounting()使用动态方式创建计数型信号量
xSemaphoreCreateCountingStatic()使用静态方式创建计数型信号量
xSemaphoreTake()获取信号量
xSemaphoreTakeFromISR()在中断中获取信号量
xSemaphoreGive()释放信号量
xSemaphoreGiveFromISR()在中断中释放信号量
vSemaphoreDelete()删除信号量
uxSemaphoreGetCount获取信号量的计数值

创建计数型信号量:xSemaphoreCreateCounting(uxMaxCount, uxInitialCount)其实调用函数xQueueCreateCountingSemaphore( ( uxMaxCount ), ( uxInitialCount ))

@ uxMaxCount:计数值的最大限定
@uxInitialCount:计数值的初始值
返回值:NULL:失败;创建成功返回计数型信号量句柄

 获取信号量的计数值:uxSemaphoreGetCount()其实调用函数uxQueueMessagesWaiting(QueueHandle_t xSemaphore)
@xSemaphore:句柄
返回值:当前信号量计数值大小

3.4 优先级翻转 

优先级翻转:当一个高优先级任务因获取一个被低优先级任务获取而处于没有资源状态的二值信号量时,这个高优先级的任务将被阻塞,直到低优先级的任务释放二值信号量,而在这之前,如果有一个优先级介于这个高优先级任务和低优先级任务之间的任务就绪,那么这个中等优先级的任务就会抢占低优先级任务的运行, 这么一来,这三个任务中优先级最高的任务反而要最后才运行

在使用二值信号量和计数型信号量的时候,经常会遇到优先级翻转的问题,优先级在抢占式内核中是非常常见的,但是在实时操作系统中是不允许出现优先级翻转的,因为优先级翻转会破坏任务的预期顺序,可能会导致未知的严重后果,下面展示了一个优先级翻转的例子

例如:

 前提条件:
任务H优先级最高,其先进行阻塞,获取信号量,释放信号量
任务M优先级中等,其先进行阻塞,循环打印
任务L优先级最低,其先运行,获取信号量,释放信号量

任务L先运行,获取信号量后,信号量为0;此时任务H就绪,抢占任务L,任务H进行获取信号量,但是由于信号量由任务L获取后未释放,所以目前H无法获得信号量而进行阻塞,此时任务L就绪运行,运行过程中任务M就绪对任务L进行抢占并运行,运行完毕后阻塞,由于任务L没有释放信号量,任务H一直被阻塞,任务L运行,释放信号量,任务H立刻获取信号量;也即是说当任务L阻塞时,优先级最高的任务L应该时执行的,但是由于任务L把持这信号量,导致任务H因等待信号量被阻塞,被任务M抢占运行。 

高优先级任务被低优先级任务阻塞,导致高优先级任务迟迟得不到调度,但其他中等优先级的任务可以抢占CPU资源,从现象看就像中等优先级的任务比高优先级的任务有更高的优先权(即优先级翻转)

3.5 互斥信号量

3.5.1 简介 

互斥信号量其实就是一个拥有优先级继承的二值信号量,在同步的应用中(任务与任务或中断与任务之间的同步)二值信号量最适合。互斥信号量适合用于那些需要互斥访问的应用中。在互斥访问中互斥信号量相当于一把钥匙, 当任务想要访问共享资源的时候就必须先获得这把钥匙,当访问完共享资源以后就必须归还这把钥匙,这样其他的任务就可以拿着这把钥匙去访问资源。

当一个互斥信号量正在被一个低优先级的任务持有时, 如果此时有个高优先级的任务也尝试获取这个互斥信号量,那么这个高优先级的任务就会被阻塞。不过这个高优先级的任务会将低优先级任务的优先级提升到与自己相同的优先级,这个过程就是优先级继承

优先级继承尽可能的减少了高优先级任务处于阻塞态的时间,并且将“优先级翻转”的影响降到最低。

优先级继承并不能完全的消除优先级翻转的问题,它只是尽可能的降低优先级翻转带来的影响。实时应用应该在设计之初就要避免优先级翻转的发生

互斥信号量不能用于中断服务函数中,原因如下:

(1) 互斥信号量有任务优先级继承的机制, 但是中断不是任务,没有任务优先级, 所以互斥信号量只能用与任务中,不能用于中断服务函数。

(2) 中断服务函数中不能因为要等待互斥信号量而设置阻塞时间进入阻塞态。

3.5.2 相关API

使用流程(与二值信号量有区别):创建互斥信号量-->获取信号量-->释放信号量

注意:创建互斥信号量是,函数内部会主动释放信号量

函数描述
xSemaphoreCreateMutex()使用动态方式创建互斥信号量
xSemaphoreCreateMutexStatic()使用静态方式创建互斥信号量
xSemaphoreTake()获取信号量
xSemaphoreGive()释放信号量
vSemaphoreDelete()删除信号量

互斥信号量的就是一个队列长度为 1 的队列, 且队列项目的大小为 0, 而队列的非空闲项目数量就是互斥信号量的资源数


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

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

相关文章

垃圾收集器-ZGC

前言在Java开发中&#xff0c;垃圾收集器的选择对系统性能有着致命的影响。Java 8后&#xff0c;虽然G1 GC成为默认&#xff0c;但是它在延迟性控制上仍有限。ZGC作为最新一代高性能低延迟垃圾收集器&#xff0c;解决了CMS和G1在延迟、垃圾堆容量和吞吐量方面的重大突破。本文将…

计算机“十万个为什么”之跨域

计算机“十万个为什么”之跨域 本文是计算机“十万个为什么”系列的第五篇&#xff0c;主要是介绍跨域的相关知识。 作者&#xff1a;无限大 推荐阅读时间&#xff1a;10 分钟 一、引言&#xff1a;为什么会有跨域这个“拦路虎”&#xff1f; 想象你正在参观一座戒备森严的城堡…

C语言:20250719笔记

字符数组在C语言中&#xff0c;支持字符串常量&#xff0c;不支持字符串变量。如果想要实现类似的字符串变量&#xff0c;C语言提供了两种实现方式&#xff1a;字符数组&#xff1a;char name[] “哪吒”&#xff1b;字符指针&#xff1a;char *name "娜吒"&#x…

decltype是什么,什么作用?

基本概念decltype 是 C11 引入的关键字&#xff0c;用于推导表达式的类型&#xff0c;且会完整保留类型的细节&#xff08;包括 const、引用 &、指针 * 等&#xff09;。语法:decltype(表达式) 变量名核心特点1.推导依据是表达式本身&#xff0c;而非表达式的结果&#xff…

RPC 与 Feign 的区别笔记

一、基本概念 1.1 RPC&#xff08;Remote Procedure Call&#xff09; 定义&#xff1a;远程过程调用&#xff0c;允许像调用本地方法一样调用远程服务的方法。 本质&#xff1a;跨进程通信&#xff0c;隐藏了底层网络通信的复杂性。 常见实现&#xff1a; Java 原生 RMIDub…

高防IP能够防御CC攻击吗?它具备哪些显著优势?

摘要&#xff1a; 面对日益复杂的网络攻击&#xff0c;高防IP作为重要的安全工具&#xff0c;不仅能防御常见的DDoS攻击&#xff0c;还能有效应对CC攻击。本文将解析高防IP防御CC攻击的原理及其核心优势&#xff0c;帮助读者了解其在网络安全中的关键作用。一、高防IP能否防御C…

TypeScript 类型注解(一)

一、TypeScript 类型注解1、什么是TpyeScript类型注解- 是否还记得TypeScript的两个重要特性&#xff1f;- 类型系统、适用于任何规模- 可以说&#xff0c;TS的类型系统是TS最重要的功能&#xff1b;那么什么是类型注解呢&#xff1f;其实就是在声明变量时&#xff0c;将变量的…

弗兰肯斯坦式的人工智能与GTM策略的崩溃

2025 年上半年已经明确了一件事&#xff1a;B2B 市场营销团队被工具淹没&#xff0c;但缺乏策略。人工智能无处不在。收入领导者在进行无休止的试点。营销团队拼凑各种点解决方案&#xff0c;希望能实现规模扩张。然而&#xff0c;销售线索的增长停滞不前。信誉正在受损。曾经承…

NAND闪存(NAND Flash)是什么?

NAND闪存(NAND Flash)是什么? NAND闪存(NAND Flash)详解 NAND闪存是一种非易失性存储介质(断电不丢失数据),广泛应用于SSD、U盘、手机存储等设备中。NAND Flash 的全称是 “Negative-AND Flash”(与非型闪存),其名称源自其底层存储单元的电路结构——基于**“与非门…

Android性能优化之UI渲染优化

一、UI渲染核心瓶颈深度解析 1. 渲染管线关键阶段阶段CPU工作GPU工作潜在卡顿点Measure计算View尺寸-嵌套布局多次测量Layout计算View位置-频繁重排(Relayout)Draw构建DisplayList指令集-复杂自定义View.onDraw()Sync & Upload资源上传到GPU内存纹理上传大图/未压缩资源Ras…

基于Spring AI Alibaba的智能知识助手系统:从零到一的RAG实战开发

&#x1f4d6; 项目概述 在人工智能快速发展的今天&#xff0c;RAG&#xff08;Retrieval-Augmented Generation&#xff09;技术已成为构建智能问答系统的核心技术。本文将详细介绍一个基于Spring AI Alibaba DashScope深度集成的智能知识助手系统的完整开发过程&#xff0c;…

VirtualBox + CentOS:启用 DHCP 获取 IPv4 地址

标题&#xff1a; VirtualBox CentOS&#xff1a;启用 DHCP 获取 IPv4 地址 日期&#xff1a; 2025-07-18 一、问题现象 最小化安装的 CentOS 7 虚拟机里敲&#xff1a; ip addr输出只有 lo 的 127.0.0.1 以及 enp0s3 的 IPv6 链路本地地址&#xff0c;没有 IPv4&#xff0…

Git

Git简介Git 是一个分布式版本控制工具&#xff0c;通常用来对软件开发过程中的源代码文件进行管理。通过Git 仓库来存储和管理这些文件&#xff0c;Git 仓库分为两种:本地仓库:开发人员自己电脑上的 Git仓库。远程仓库:远程服务器上的 Git 仓库。commit: 提交, 将本地文件和版本…

通信算法之294:LTE系统中的整数倍频偏估计

在LTE系统中&#xff0c;整数倍频偏估计主要通过以下方法实现&#xff1a;一、最大似然估计法&#xff08;ML&#xff09;通过遍历预设的整数倍频偏范围&#xff08;如30kHz&#xff09;&#xff0c;将接收信号与本地的PSS序列在不同频偏点上进行相关运算&#xff0c;选择相关峰…

数字人直播:开启直播行业新纪元​

​原始尺寸更换图片p9-flow-imagex-sign.byteimg.com​​在科技日新月异的当下&#xff0c;直播行业正经历着一场深刻变革&#xff0c;数字人直播的兴起&#xff0c;宛如一颗璀璨新星&#xff0c;照亮了直播领域的新征程。数字人直播&#xff0c;是利用先进的人工智能技术&…

朝鲜升级供应链恶意软件XORIndex,再次瞄准npm生态系统

Socket威胁研究团队最新披露&#xff0c;朝鲜国家支持的黑客组织在"传染性面试"攻击活动中采用了新型恶意软件加载器XORIndex&#xff0c;该恶意程序专门通过npm软件包注册表渗透软件供应链。攻击规模与持续性此次攻击并非孤立事件&#xff0c;而是针对开发者、求职者…

Windows 下 VS2019 编译 libevent-2.1.10 库

1. 你需要VS2019 编译好openssl-1.1.1 &#xff0c;这个具体编译或者下载可以参考我的博客openssl生成的库是这两个文件接下来&#xff0c;打开CMake &#xff0c;主要是下面的需要设置好最后Config Generate即可&#xff1b;全部成功生成 22个然后INSTALL右键生成 最后看下生…

Vim多列操作指南

我们在使用 Vim 时&#xff0c;经常需要同时编辑多个文件&#xff0c;或者同一个文件的不同部分。Vim 提供了分割窗口&#xff08;split&#xff09;和垂直分割窗口&#xff08;vsplit&#xff09;的功能&#xff0c;允许我们在同一个 Vim 会话中查看多个缓冲区&#xff08;buf…

Python网络爬虫实现selenium对百度识图二次开发以及批量保存Excel

一.百度识图自动上传图片from selenium import webdriver from selenium.webdriver.edge.options import Options from selenium.webdriver.common.by import By edge_options Options() edge_options.binary_location r"C:\Program Files (x86)\Microsoft\Edge\Applica…

Vue中的refs字段使用记录

这段代码是 Vue.js 中结合 Element UI 等 UI 库的典型表单验证写法&#xff0c;具体含义如下&#xff1a;代码拆解 this.$refs.fromData.validate((valid) > {// 验证后的回调逻辑 })this.$refs.fromData $refs 是 Vue 提供的特殊属性&#xff0c;用于访问模板中通过 ref&qu…