FreeRTOS消息队列

news2024/11/13 4:03:57

消息队列是一种常用于任务间通信的数据结构。通过消息队列服务,任务或 中断服务例程可以将一条或多条消息放入消息队列中,同样,一个或多个任务可 以从消息队列中获得消息。本章将向大家介绍 FreeRTOS 的消息队列,通过本章 的学习,让大家对操作系统中任务间数据传输更加了解。本章要实现的功能是: 创建 3 个任务,分别为 LED、消息队列发送、消息队列接收任务,LED 任务控制 DS0 指示灯闪烁,KEY_UP 和 KEY1 键控制消息队列数据发送,然后等到消息队列 有数据,则接收并通过串口输出。本章分为如下几部分内容: 6.1 消息队列简介 6.2 常用消息队列 API 函数 6.3 消息队列使用注意事项 6.4 硬件设计 6.5 软件设计 6.6 实验现 像

6.1 消息队列简介

6.1.1 消息队列概念简介 队列是为了任务与任务、任务与中断之间的通信而准备的,可以在任务与任 务、任务与中断之间传递消息,队列中可以存储有限的、大小固定的数据项目。 任务与任务、任务与中断之间要交流的数据保存在队列中,叫做队列项目。队列 所能保存的最大数据项目数量叫做队列的长度,创建队列的时候会指定数据项目 的大小和队列的长度。由于队列用来传递消息的,所以也称为消息队列。FreeRTOS 中的信号量的也是依据队列实现的,所以有必要深入的了解 FreeRTOS 的队列。

6.1.2 消息队列数据存储 通常队列采用先进先出(FIFO)的存储缓冲机制,也就是往队列发送数据的时 候(也叫入队)永远都是发送到队列的尾部,而从队列提取数据的时候(也叫出队) 是从队列的头部提取的。但是也可以使用 LIFO 的存储缓冲,也就是后进先出, FreeRTOS 中的队列也提供了 LIFO 的存储缓冲机制。 数据发送到队列中会导致数据拷贝,也就是将要发送的数据拷贝到队列中, 这就意味着在队列中存储的是数据的原始值,而不是原数据的引用(即只传递数 据的指针),这个也叫做值传递。学过 UCOS 的同学应该知道,UCOS 的消息队列 采用的是引用传递,传递的是消息指针。采用引用传递的话消息内容就必须一直 保持可见性,也就是消息内容必须有效,那么局部变量这种可能会随时被删掉的 东西就不能用来传递消息,但是采用引用传递会节省时间!因为不用进行数据拷 贝。 采用值传递的话虽然会导致数据拷贝,会浪费一点时间,但是一旦将消息发 送到队列中原始的数据缓冲区就可以删除掉或者覆写,这样的话这些缓冲区就可 以被重复的使用。FreeRTOS 中使用队列传递消息的话虽然使用的是数据拷贝, 但是也可以使用引用来传递消息,直接往队列中发送指向这个消息的地址指针就 可以了!这样当要发送的消息数据太大的时候就可以直接发送消息缓冲区的地址 指针,比如在网络应用环境中,网络的数据量往往都很大的,采用数据拷贝的话 就不现实。

6.1.3 消息队列阻塞机制

6.1.3.1 出队阻塞

当任务尝试从一个队列中读取消息的时候可以指定一个阻塞时间,这个阻塞 时间就是当任务从队列中读取消息无效的时候任务阻塞的时间。出队就是从队列 中读取消息,出队阻塞是针对从队列中读取消息的任务而言的。比如任务 A 用 于处理串口接收到的数据,串口接收到数据以后就会放到队列 Q 中,任务 A 从 队列 Q 中读取数据。但是如果此时队列 Q 是空的,说明还没有数据,任务 A 这 时候来读取的话肯定是获取不到任何东西,那该怎么办呢?任务 A 现在有三种 选择,一:二话不说扭头就走,二:要不我在等等吧,等一会看看,说不定一会 就有数据了,三:死等,死也要等到你有数据!选哪一个就是由这个阻塞时间决 定的,这个阻塞时间单位是时钟节拍数。阻塞时间为 0 的话就是不阻塞,没有 数据的话就马上返回任务继续执行接下来的代码,对应第一种选择。如果阻塞时 间为 0~ portMAX_DELAY 之间,当任务没有从队列中获取到消息的话就进入阻塞 态,阻塞时间指定了任务进入阻塞态的时间,当阻塞时间到了以后还没有接收到 数据的话就退出阻塞态,返回任务接着运行下面的代码,如果在阻塞时间内接收 到了数据就立即返回,执行任务中下面的代码,这种情况对应第二种选择。当阻 塞时间设置为 portMAX_DELAY 的话,任务就会一直进入阻塞态等待,直到接收 到数据为止!这个就是第三种选择。

6.1.3.2 入队阻塞

入队说的是向队列中发送消息,将消息加入到队列中。和出队阻塞一样,当 一个任务向队列发送消息的话也可以设置阻塞时间。比如任务 B 向消息队列 Q 发送消息,但是此时队列 Q 是满的,那肯定是发送失败的。此时任务 B 就会遇 到和上面任务 A 一样的问题,这两种情况的处理过程是类似的,只不过一个是 向队列 Q 发送消息,一个是从队列 Q 读取消息而

2 常用消息队列 API 函数

使用队列模块的典型流程如下: ●创建消息队列。 ●写队列操作。 ●读队列操作。 ●删除队列。

.1 消息队列创建函数 xQueueCreate() xQueueCreate()用于创建一个新的队列并返回可用于访问这个队列的队列 句柄。队列句柄其实就是一个指向队列数据结构类型的指针。 队列就是一个数据结构,用于任务间的数据的传递。每创建一个新的队列都 需要为其分配 RAM,一部分用于存储队列的状态,剩下的作为队列消息的存储区 域。使用 xQueueCreate()创建队列时,使用的是动态内存分配,所以要想使用 该函数必须在 FreeRTOSConfig.h 中把 configSUPPORT_DYNAMIC_ALLOCATION 定义为 1 来使能,这是个用于使能动态内存分配的宏,通常情况下,在 FreeRTOS 中,凡是创建任务,队列,信号量和互斥量等内核对象都需要使用动态内存分配, 所以这个宏默认在 FreeRTOS.h 头文件中已经使能(即定义为 1)。如果想使用 静态内存,则可以使用 xQueueCreateStatic() 函数来创建一个队列。使用静态 创建消息队列函数创建队列时需要的形参更多,需要的内存由编译的时候预先分 配好,一般很少使用这种方法

.2 消息队列静态创建函数 xQueueCreateStatic() xQueueCreateStatic()用于创建一个新的队列并返回可用于访问这个队列 的队列句柄。队列句柄其实就是一个指向队列数据结构类型的指针。 队列就是一个数据结构,用于任务间的数据的传递。每创建一个新的队列都 需要为其分配 RAM,一部分用于存储队列的状态,剩下的作为队列的存储区。使 用 xQueueCreateStatic()创建队列时,使用的是静态内存分配,所以要想使用 该函数必须在 FreeRTOSConfig.h 中把 configSUPPORT_STATIC_ALLOCATION 定 义为 1 来使能。这是个用于使能静态内存分配的宏,需要的内存在程序编译的 时候分配好,由用户自己定义,其实创建过程与 xQueueCreate()都是差不多的, 我们暂不深入讲解。xQueueCreateStatic()函数的具体说明如下。

3 消息队列删除函数 vQueueDelete() 队列删除函数是根据消息队列句柄直接删除的,删除之后这个消息队列的所 有信息都会被系统回收清空,而且不能再次使用这个消息队列了,但是需要注意 的是,如果某个消息队列没有被创建,那也是无法被删除的,动脑子想想都知道, 没创建的东西就不存在,怎么可能被删除。xQueue 是 vQueueDelete()函数的形 参,是消息队列句柄,表示的是要删除哪个想队列

.4 向消息队列发送消息函数

任务或者中断服务程序都可以给消息队列发送消息,当发送消息时,如果队 列未满或者允许覆盖入队,FreeRTOS 会将消息拷贝到消息队列队尾,否则,会 根据用户指定的阻塞超时时间进行阻塞,在这段时间中,如果队列一直不允许入 队,该任务将保持阻塞状态以等待队列允许入队。当其它任务从其等待的队列中 读取入了数据(队列未满),该任务将自动由阻塞态转为就绪态。当任务等待的 时间超过了指定的阻塞时间,即使队列中还不允许入队,任务也会自动从阻塞态 转移为就绪态,此时发送消息的任务或者中断程序会收到一个错误码 errQUEUE_FULL。 发送紧急消息的过程与发送消息几乎一样,唯一的不同是,当发送紧急消息 时,发送的位置是消息队列队头而非队尾,这样,接收者就能够优先接收到紧急 消息,从而及时进行消息处理。 其实消息队列发送函数有好几个,都是使用宏定义进行展开的,有些只能在 任务调用,有些只能在中断中调用,具体见下面讲解

.4.1 xQueueSend()与 xQueueSendToBack( )

#define xQueueSend( xQueue, pvItemToQueue, xTicksToWait )
xQueueGenericSend( ( xQueue ), ( pvItemToQueue ),
( xTicksToWait ), queueSEND_TO_BACK )
#define xQueueSendToBack( xQueue, pvItemToQueue, xTicksToWait )
xQueueGenericSend( ( xQueue ), ( pvItemToQueue ),
( xTicksToWait ), queueSEND_TO_BACK )

xQueueSend()是一个宏,宏展开是调用函数 xQueueGenericSend(),这个函 数在后面会详细讲解其实现过程。该宏是为了向后兼容没有包含 xQueueSendToFront() 和 xQueueSendToBack() 这两个宏的 FreeRTOS 版本。 xQueueSend()等同于 xQueueSendToBack()。 xQueueSend()用于向队列尾部发送一个队列消息。消息以拷贝的形式入队, 而不是以引用的形式。该函数绝对不能在中断服务程序里面被调用,中断中必须 使用带有中断保护功能的 xQueueSendFromISR()来代替。xQueueSend()函数的具 体说明见如下。

.4.2 xQueueSendFromISR()与 xQueueSendToBackFromISR()

#define xQueueSendToBackFromISR( xQueue, pvItemToQueue,
pxHigherPriorityTaskWoken )
xQueueGenericSendFromISR( ( xQueue ), ( pvItemToQueue ),
( pxHigherPriorityTaskWoken ),
queueSEND_TO_BACK )
#define xQueueSendFromISR( xQueue, pvItemToQueue,
pxHigherPriorityTaskWoken )
xQueueGenericSendFromISR( ( xQueue ), ( pvItemToQueue ),
( pxHigherPriorityTaskWoken ),
queueSEND_TO_BACK )

xQueueSendToBackFromISR 等同于 xQueueSendFromISR ()。 xQueueSendFromISR()是一个宏,宏展开是调用函数 xQueueGenericSendFromISR()。该宏是 xQueueSend()的中断保护版本,用于在 中断服务程序中向队列尾部发送一个队列消息,等价于 xQueueSendToBackFromISR()。xQueueSendFromISR()函数具体说明如下。

.4.3 xQueueSendToFro

#define xQueueSendToFront( xQueue, pvItemToQueue, xTicksToWait )
xQueueGenericSend( ( xQueue ), ( pvItemToQueue ),
( xTicksToWait ), queueSEND_TO_FRONT )

xQueueSendToFron()是一个宏,宏展开也是调用函数 xQueueGenericSend()。 xQueueSendToFront()用于向队列队首发送一个消息。消息以拷贝的形式入队, 而不是以引用的形式。该函数绝不能在中断服务程序里面被调用,而是必须使用 带有中断保护功能的 xQueueSendToFrontFromISR ()来代替。xQueueSendToFron() 函数的具体说明如下,使用方式与 xQueueSend()函数一致

.4.4 xQueueSendToFrontFromISR()

#define xQueueSendToFrontFromISR( xQueue, pvItemToQueue,
pxHigherPriorityTaskWoken )
xQueueGenericSendFromISR( ( xQueue ), ( pvItemToQueue ),
( pxHigherPriorityTaskWoken ),
queueSEND_TO_FRONT )

xQueueSendToFrontFromISR()是一个宏,宏展开是调用函数 xQueueGenericSendFromISR()。该宏是 xQueueSendToFront()的中断保护版本, 用于在中断服务程序中向消息队列队首发送一个消息。xQueueSendToFromISR() 函数具体说明如下,使用方式与 xQueueSendFromISR()函数一致

4.5 xQueueGenericSend()

上面看到的那些在任务中发送消息的函数都是 xQueueGenericSend()展开 的宏定义,真正起作用的就是 xQueueGenericSend()函数,根据指定的参数不一 样,发送消息的结果就不一样,

4.6 xQueueGenericSendFromISR()

既然有任务中发送消息的函数,当然也需要有在中断中发送消息函数,其实 这个函数跟 xQueueGenericSend() 函数很像,只不过是 执行的上下文环境是不 一样的,xQueueGenericSendFromISR()函数只能用于中断中执行,是不带阻塞机 制的,

.5 从消息队列读取消息函数

5.1 xQueueReceive()与 xQueuePeek()

#define xQueueReceive( xQueue, pvBuffer, xTicksToWait )
xQueueGenericReceive( ( xQueue ), ( pvBuffer ),
( xTicksToWait ), pdFALSE) 

xQueueReceive() 是一个宏,宏展开是调用函数 xQueueGenericReceive()。 xQueueReceive()用于从一个队列中接收消息并把消息从队列中删除。接收的消 息是以拷贝的形式进行的,所以我们必须提供一个足够大空间的缓冲区。具体能 够拷贝多少数据到缓冲区,这个在队列创建的时候已经设定。该函数绝不能在中 断服务程序里面被调用,而是必须使用带有中断保护功能的 xQueueReceiveFromISR ()来代替。xQueueReceive()函数的具体说明如下。

看到这里,有人就问了如果我接收了消息不想删除怎么办呢?其实,你能想 到的东西,FreeRTOS 也想到了,如果不想删除消息的话,就调用 xQueuePeek() 函数。 其实这个函数与 xQueueReceive()函数的实现方式一样,连使用方法都一 样,只不过 xQueuePeek()函数接收消息完毕不会删除消息队列中的消息而已, 函数原型具体如下。

#define xQueuePeek( xQueue, pvBuffer, xTicksToWait )
xQueueGenericReceive( ( xQueue ), ( pvBuffer ),
( xTicksToWait ), pdTRUE )

.5.2 xQueueReceiveFromISR()与 xQueuePeekFromISR()

xQueueReceiveFromISR()是 xQueueReceive ()的中断版本,用于在中断服 务程序中接收一个队列消息并把消息从队列中删除;xQueuePeekFromISR()是 xQueuePeek()的中断版本,用于在中断中从一个队列中接收消息,但并不会把消 息从队列中移除。 说白了这两个函数只能用于

6.3 消息队列使用注意事项

在使用 FreeRTOS 提供的消息队列函数的时候,需要了解以下几点: 1.使用 xQueueSend()、xQueueSendFromISR()、xQueueReceive()等这些函 数之前应先创建需消息队列,并根据队列句柄进行操作。 2.队列读取采用的是先进先出(FIFO)模式,会先读取先存储在队列中的数 据。当然也 FreeRTOS 也支持后进先出(LIFO)模式,那么读取的时候就会读取 到后进队列的数据。 3.在获取队列中的消息时候,我们必须要定义一个存储读取数据的地方,并 且该数据区域大小不小于消息大小,否则,很可能引发地址非法的错误。 4.无论是发送或者是接收消息都是以拷贝的方式进行,如果消息过于庞大, 可以将消息的地址作为消息进行发送、接收。 5.队列是具有自己独立权限的内核对象,并不属于任何任务。所有任务都可 以向同一队列写入和读出。一个队列由多任务或中断写入是经常的事,但由多个 任务读出倒是用的比较少

整体代码

#include "system.h"
#include "SysTick.h"
#include "led.h"
#include "usart.h"
#include "FreeRTOS.h"
#include "task.h"
#include "key.h"
#include "queue.h"


//任务优先级
#define START_TASK_PRIO        1
//任务堆栈大小    
#define START_STK_SIZE         128  
//任务句柄
TaskHandle_t StartTask_Handler;
//任务函数
void start_task(void *pvParameters);

//任务优先级
#define LED1_TASK_PRIO        2
//任务堆栈大小    
#define LED1_STK_SIZE         50  
//任务句柄
TaskHandle_t LED1Task_Handler;
//任务函数
void led1_task(void *pvParameters);

//任务优先级
#define RECEIVE_TASK_PRIO        3
//任务堆栈大小    
#define RECEIVE_STK_SIZE         50  
//任务句柄
TaskHandle_t ReceiveTask_Handler;
//任务函数
void receive_task(void *pvParameters);

//任务优先级
#define SEND_TASK_PRIO        4
//任务堆栈大小    
#define SEND_STK_SIZE         50  
//任务句柄
TaskHandle_t SendTask_Handler;
//任务函数
void send_task(void *pvParameters);


QueueHandle_t Test_Queue =NULL;

#define  QUEUE_LEN    4   /* 队列的长度,最大可包含多少个消息 */
#define  QUEUE_SIZE   4   /* 队列中每个消息大小(字节) */


/*******************************************************************************
* 函 数 名         : main
* 函数功能           : 主函数
* 输    入         : 无
* 输    出         : 无
*******************************************************************************/
int main()
{
    SysTick_Init(72);
    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);//设置系统中断优先级分组4
    LED_Init();
    KEY_Init();
    USART1_Init(115200);
    printf("FreeRTOS消息队列实验\r\n");
    printf("按下KEY_UP或者KEY1发送队列消息\r\n");
    printf("Receive任务接收到消息在串口回显\r\n");
    //创建开始任务
    xTaskCreate((TaskFunction_t )start_task,            //任务函数
                (const char*    )"start_task",          //任务名称
                (uint16_t       )START_STK_SIZE,        //任务堆栈大小
                (void*          )NULL,                  //传递给任务函数的参数
                (UBaseType_t    )START_TASK_PRIO,       //任务优先级
                (TaskHandle_t*  )&StartTask_Handler);   //任务句柄              
    vTaskStartScheduler();          //开启任务调度
}

//开始任务任务函数
void start_task(void *pvParameters)
{
    taskENTER_CRITICAL();           //进入临界区
     
    /* 创建Test_Queue */
    Test_Queue = xQueueCreate((UBaseType_t ) QUEUE_LEN,/* 消息队列的长度 */
                            (UBaseType_t ) QUEUE_SIZE);/* 消息的大小 */
    
    //创建LED1任务
    xTaskCreate((TaskFunction_t )led1_task,     
                (const char*    )"led1_task",   
                (uint16_t       )LED1_STK_SIZE, 
                (void*          )NULL,
                (UBaseType_t    )LED1_TASK_PRIO,
                (TaskHandle_t*  )&LED1Task_Handler); 
                
    //创建接收任务
    xTaskCreate((TaskFunction_t )receive_task,     
                (const char*    )"receive_task",   
                (uint16_t       )RECEIVE_STK_SIZE, 
                (void*          )NULL,
                (UBaseType_t    )RECEIVE_TASK_PRIO,
                (TaskHandle_t*  )&ReceiveTask_Handler);

    //创建发送任务
    xTaskCreate((TaskFunction_t )send_task,     
                (const char*    )"send_task",   
                (uint16_t       )SEND_STK_SIZE, 
                (void*          )NULL,
                (UBaseType_t    )SEND_TASK_PRIO,
                (TaskHandle_t*  )&SendTask_Handler);
                
    vTaskDelete(StartTask_Handler); //删除开始任务
    taskEXIT_CRITICAL();            //退出临界区
} 

//LED1任务函数
void led1_task(void *pvParameters)
{
    while(1)
    {
        LED1=0;
        vTaskDelay(200);
        LED1=1;
        vTaskDelay(800);
    }
}

//接收任务函数
void receive_task(void *pvParameters)
{
    BaseType_t xReturn = pdTRUE;/* 定义一个创建信息返回值,默认为pdTRUE */
    uint32_t r_queue;    /* 定义一个接收消息的变量 */
    
    while(1)
    {
        xReturn = xQueueReceive( Test_Queue,    /* 消息队列的句柄 */
                                &r_queue,      /* 发送的消息内容 */
                                portMAX_DELAY); /* 等待时间 一直等 */
        if(pdTRUE == xReturn)
            printf("本次接收到的数据是%d\n\n",r_queue);
        else
            printf("数据接收出错,错误代码0x%lx\n",xReturn);
    }
}

//发送任务函数
void send_task(void *pvParameters)
{
    BaseType_t xReturn = pdPASS;/* 定义一个创建信息返回值,默认为pdPASS */
    uint32_t send_data1 = 1;
    uint32_t send_data2 = 2;
    u8 key=0;
    
    while(1)
    {
        key=KEY_Scan(0);
        if(key==KEY_UP_PRESS)
        {
            printf("发送消息send_data1!\n");
            xReturn = xQueueSend( Test_Queue, /* 消息队列的句柄 */
                                &send_data1,/* 发送的消息内容 */
                                0 );        /* 等待时间 0 */
            if(pdPASS == xReturn)
                printf("消息send_data1发送成功!\n\n");
        }
        else if(key==KEY1_PRESS)
        {
            printf("发送消息send_data2!\n");
            xReturn = xQueueSend( Test_Queue, /* 消息队列的句柄 */
                                &send_data2,/* 发送的消息内容 */
                                0 );        /* 等待时间 0 */
            if(pdPASS == xReturn)
                printf("消息send_data2发送成功!\n\n");
        }
        vTaskDelay(20);
    }
}

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

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

相关文章

238. 除自身以外数组的乘积

【答案解析】:暴力不考虑其他的因素的话,将所有数据乘积起来,然后遍历数组除以当前位置数据即可。更优解法:将乘积分为两次进行,第一次先将每个位置左边的数据乘积计算出来放到返回数组中,后边第二次循环 将…

【Java面试(二)】冒泡排序的实现及优化

文章目录前言冒泡排序初步实现冒泡排序_优化_减少比较次数冒泡排序_优化_减少冒泡次数冒泡排序_优化_进一步优化比较次数总结前言 今天我们来学习与排序相关的面试题,首先我们先来学习冒泡排序,那什么是冒泡排序呢,它的关键在于数组中相邻元素…

变聪明的方法就是学习,每个人最终都会与生活和解,连村西透仿佛也捡回了昔日的勇气。

文章目录❤️‍🔥 序❤️‍🔥 往事如风迹难寻 - 成为创作者的契机❤️‍🔥 新星计划遇善者 - 出道即是巅峰MVP❤️‍🔥 知行合一心依旧 - 初心不改坚持创作❤️‍🔥 知易行难搞规划 - 这是不能说的秘密❤️‍&#x1f…

中国为何就不能有自己的豪华MPV?

文|智能相对论作者| 陈明涛是时候重新认识海外汽车品牌和国产汽车品牌的MPV了。去年,曾有雷克萨斯LM在碰撞后出现全车车门无法打开,之后车辆起火酿成悲剧,引发了全网对MPV碰撞安全的高度关注。前段时间丰田埃尔法再登热搜,这次不是…

2023最新Python国内镜像源,亲测可用

1、镜像源 pip包管理工具可以下载第三方库到本地使用,第三方库的来源地址称之为镜像源,镜像源中存放了大量的开源库供我们下载使用。pip的默认镜像源地址在国外,下载很慢,本文收集了当前国内常用的镜像源,速率由快到慢…

Java多线程 - 定时器-并发与并行-线程生命周期

文章目录多线程补充定时器并发和并行线程的生命周期多线程补充 定时器 定时器介绍: 定时器是一种控制任务延时调用,或者周期调用的技术。 作用:闹钟、定时邮件发送。 定时器实现方式: 方式一:Timer 方式二: ScheduledExecutorSe…

java程序报错后的排错思路

目前总结出来三个字:看日志! 而且是从左到右一个单词一个单词的看。   举个例子:   Spring框架下的一个Demo,启动时报出了以下错误。 一、看异常类型   首先,能看到异常是从引入的SpringFramework依赖中报出来的&#xf…

[数据结构基础]排序算法第三弹 -- 快速排序

目录 一. 快速排序的基本思想 二. 快速排序的递归实现 2.1 单趟快速排序的实现 2.1.1 Hoare法实现单趟快排 2.1.2 挖坑法实现单趟快排 2.1.3 前后指针法实现单趟快排 2.2 递归快排的整体实现 三. 快速排序的时间复杂度分析 四. 快速排序的非递归实现 4.1 快速排序非递…

Promise详解与手写实现

Promise详解与手写实现Promise1、Promise介绍与基本使用1.1 Promise概述1.2 Promise的作用1.3 Promise的使用2、Promise API3、Promise关键问题4、Promise自定义封装5、async与await5.1. mdn文档5.2.async函数5.3.await表达式5.4.注意Promise 1、Promise介绍与基本使用 1.1 P…

电商维权,维权方法汇总【超全】

电商维权,就是维护线上渠道中自己的合法权益,其中包括消费者维权、品牌方维权、卖家维权。今天,我们来聊一聊消费者维权。 1、 维权类型 消费者在网购过程中难免遇到各种问题,主要就是产品质量问题、产品价格问题、产品售后问题、…

JavaWeb-VUEElement

JavaWeb-VUE&Element 1,VUE 1.1 概述 Vue 是一套前端框架,免除原生JavaScript中的DOM操作,简化书写。 Mybatis 是用来简化 jdbc 代码编写的;而 VUE 是前端的框架,是用来简化 JavaScript 代码编写的。前一天我们…

PID控制的方波响应

被控对象为一延迟对象:采样时间为20s,延迟时间为4个采样时间,即 80s,被控对象离散化为:y(k) -den(2)y(k- 1)num(2)u(k - 5)由于方波信号的速度、加速度不连续,当位置跟踪指令为方波信号时,如采用…

C++ 树进阶系列之树状数组的树形之路

1. 前言 树状数组也称二叉索引树,由Peter M. Fenwick于1994发明,也可称为Fenwick树。 树状数组的设计非常精巧,多用于求解数列的前缀和、区间和等问题,为区间类型问题提供了模板式解决方案。 数状数组简单易用,但对…

【100个 Unity实用技能】 | 修改Unity UI控件中默认字体配置

Unity 小科普 老规矩,先介绍一下 Unity 的科普小知识: Unity是 实时3D互动内容创作和运营平台 。包括游戏开发、美术、建筑、汽车设计、影视在内的所有创作者,借助 Unity 将创意变成现实。Unity 平台提供一整套完善的软件解决方案&#xff…

【C++提高编程1】一文带你吃透函数模板和类模板(附测试用例源码、测试结果图及注释)

📝我的个人主页 🎁欢迎各位→点赞👍 收藏⭐️ 留言📝​💬总结:希望你看完之后,能对你有所帮助,不足请指正!共同学习交流 🖊✉️今天你做别人不想做的事&…

域内权限维持:注入SSP

01、简介 SSP(Security Support Provider)是Windows操作系统安全机制的提供者。简单地说,SSP是个DLL文件,主要用来实现Windows操作系统的身份认证功能。在系统启动时,SSP 将被加载到lsass.exe进程中,攻击者通过自定义恶意的DLL文件…

解决ModuleNotFoundError: No module named ‘torch.fx‘

运行yolo v5 train python train.py 报错 ModuleNotFoundError: No module named ‘torch.fx’ torch版本不匹配 目前版本torchu1.7 #卸载pytorch pip uninstall torch 再安装 python -m pip install torch -i https://mirrors.aliyun.com/pypi/simple/ python -m pip是…

本周大新闻|Quest Pro降价至1099美元,传苹果AIGC或用于XR内容生成

本周大新闻正值春节假期,因此包含近两周(1月16-1月29日)的AR/VR新闻汇总。关于2022,近期我们发布了2022年AR/VR行业融资报告、2022年AR硬件总结、2022年VR硬件总结。AR方面,最新消息称苹果AIGC曝光,或用Sir…

通信数据中心供电系统故障影响区域分析定位

(华北石油通信有限公司)摘要:供电系统对于通信机房而言至关重要,一旦供电系统发生严重故障,需要快速制定出应急预案,使故障影响可控。本文提供一种对机房供电系统故障影响区域快速定位方法。该方法的实现思…

可观察性和安全性融合的紧迫性越来越高

根据一份新报告,融合可观察性和安全性的紧迫性越来越大。 软件情报公司 Dynatrace 公布了一项针对大型组织的 1,300 名 CIO 和高级 DevOps 经理(包括来自澳大利亚的 100 名)进行的独立全球调查的结果。 调查结果表明,随着对连…