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

FreeRTOS--消息队列

一、简介

消息队列是FreeRTOS中用于任务与任务或任务与中断之间数据交换的一种机制,采用FIFO(先进先出)方式管理数据,也可以采用LIFO(后进先出)方式。有点类似全局变量。

1.1 那为什么不直接使用全局变量,原因如下:

        1)假设有一个全局变量 a ,初始化为 0,接下来有两个任务对他进行自加操作:

 在任务1进行 a++ 时,执行到了第二步也就是加 1,此时任务2 抢占了任务1进行a++,执行完任务2 后又回到了任务1,执行最后的第三步写数据,由于在任务1执行第一步读数据的时候,a的值还没发生改变,中途被任务1抢占后,a的值改变了,但是并不会引起 r0 数据的改变,所以最后写数据,写入 a 的是 1 而不是 2。所以当两个任务各自执行一次后,并不是想象中的 +2 ,而是 +1。这是多个任务同时对这个变量进行操作,导致了数据受损。

        2)使用消息队列的情况如下:

对于消息队列进行读写操作时,他会进入临界区也就是关闭中断 ,他会在函数内部关闭FreeRTOS所管理的中断,包括(PendSV任务切换中断),这样就不能进行任务切换了,保证同一时间只有一个任务对消息队列进行访问,也就是同一时间只有一个任务可以对消息队列进行数据的读写操作。

1.2 由于FreeRTOS 的信号量(包括二值信号量、计数信号量、互斥量等)都是基于消息队列实现的功能。所以我们多多少少还是要了解一下消息队列的。

在队列中可以存储数量有限、大小固定的数据。队列中的每一个数据叫做“队列项目”,队列能够存储“队列项目”的最大数量称为队列的长度。在物理结构上有点类似于一个数组:

 

    1.3 消息队列有下面的特点

    • 入队出队:队列通常采用 “先进先出” (FIFO)的数据存储缓冲机制,也可以配置为 “后进先出” (LIFO)方式,不同方式其实就是读数据函数与写数据函数的不同搭配使用

    • 数据传输:FreeRTOS中队列采用实际值传递,即将数据拷贝到队列中进行传递,FreeRTOS采用拷贝数据传递,也可以传递指针,所以在传递较大的数据的时候采用指针传递

    • 多任务访问:队列不属于某个任务,任何任务和中断都可以向队列发送 / 读取消息

    • 出队、入队阻塞:当任务向一个队列发送消息时,可以指定一个阻塞时间,假设当前队列已满无法入队。

      • 0:阻塞时间为0,直接返回不会等待

      • 0~port_MAX_DELAY:等待设定的阻塞时间,超时后直接返回不再等待

      • port_MAX_DELAY:死等,一直等到可以入队为止,出队阻塞与入队阻塞类似、

    队列的阻塞情况如下:

            1)队列满了的时候,写入数据

    由于队列中已经有数据的并且满了,此时写入则会阻塞:

    • 将该任务的状态列表项挂载pxDelayedTaskList列表中;
    • 将该任务的事件列表项挂载xTasksWaitingToSend列表中;

            2)队列为空时,读取数据

    可以看到队列是空的,那么这时候读取数据的话也会阻塞:

    • 将该任务的状态列表项挂载pxDelayedTaskList列表中;
    • 将该任务的事件列表项挂载xTasksWaitingToReceive列表中;

    1.4 队列操作流程

    创建了一个用于任务 A 与任务 B 之间“沟通交流”的队列,这个队列最大可容纳 5 个队列项目,即队列的长度为 5。刚创建的队列是不包含内容的,因此这个队列为空。


     

    任务 A 将一个私有变量写入队列的尾部。由于在写入队列之前,队列是空的,因此新写入的消息,既是是队列的头部,也是队列的尾部


     

     任务 A 改变了私有变量的值,并将新值写入队列。现在队列中包含了队列 A写入的两个值,其中第一个写入的值在队列的头部,而新写入的值在队列的尾部。 这个尾部不是物理上的尾部,而是逻辑上的尾部。

    1.5 如果在队列满的时候,有多个任务同时对队列进行写入操作会怎么样

    1. 如果出现了空闲空间,并且优先级不一样,那么高优先级的先写入 
    2. 在优先级相等的情况下,阻塞时间最久的任务先写入

    二、队列结构体

    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;
    •  pcHead:队列使用的内存可以分为两份,该指针变量指向的是队列项的起始地址
      • 第一份是队列结构体所占用的内存
      • 第二份为所有队列项占用的内存
    • pcWriteTo:用来指向下一个写入的队列项。
    • union u:当用于队列时,使用第一个。用于互斥信号量或者递归互斥信号量时,使用第二个。
    • xTasksWaitingToSend:当在队列为满,任务继续写入,则会将任务控制块的事件列表项挂入。
    • xTasksWaitingToReceive:与上面差不多,只不过是挂载到等待接收列表。
    • cRxLock:记录队列锁定期间被移除(接收)的队列项目数量。
    • cTxLock:记录队列锁定期间被添加(发送)的队列项目数量。

    2.2 共用体的成员结构体:

    /* 用于队列时 */
    typedef struct QueuePointers
    {int8_t * pcTail;                   /* 存储区的结束地址 */int8_t * pcReadFrom;               /* 最后一个读取队列的地址 */
    } QueuePointers_t;/* 用于互斥信号量和递归互斥信号量时 */
    typedef struct SemaphoreData
    {TaskHandle_t xMutexHolder;         /* 互斥信号量的持有者 */UBaseType_t uxRecursiveCallCount;  /* 递归互斥信号量的获取计数值 */
    } SemaphoreData_t;

    2.3 队列结构体示意图:

    三、队列相关API函数

    3.1 队列创建

    /* 动态方式创建队列 */
    xQueueCreate( uxQueueLength,                /* 队列长度 */ uxItemSize );                 /* 队列项大小,单位:字节 *//* 静态方式创建队列 */
    xQueueCreateStatic( uxQueueLength,          /* 队列长度 */uxItemSize,             /* 队列项大小 */pucQueueStorage,        /* 指向预分配的队列存储区首地址 */pxQueueBuffer );        /* 指向队列结构体地址 */

    上面两个函数都是宏函数,下面只是对动态方式创建进行说明,关于动态创建的宏函数定义如下:

     动态创建函数原型为:

    QueueHandle_t xQueueGenericCreate( const UBaseType_t uxQueueLength,const UBaseType_t uxItemSize,const uint8_t ucQueueType )

    也就是 xQueueCreate() 函数其实是通过调用 xQueueGenericCreate() 函数实现的,并且对于信号量创建动态创建函数本质上也是调用 xQueueGenericCreate() 函数实现的,下面只讲解参数,具体实现自己去看源码。

            相关说明:

    • 想要使用宏函数 xQueueCreate(),需要将 configSUPPORT_DYNAMIC_ALLOCATION 置 1
    • uxQueueLength:创建队列的长度
    • uxItemSize:队列项的大小
    • ucQueueType :使用动态方式创建指定类型的队列,前面说 FreeRTOS 基于队列实现
      了多种功能,每一种功能对应一种队列类型,具体代码如下:
    #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 )      /* 递归互斥信号量 */

     3.2 队列写入消息

    /* 往队列的尾部写入消息 */
    xQueueSend( xQueue, pvItemToQueue, xTicksToWait )
    /* 同 xQueueSend() */
    xQueueSendToBack( xQueue, pvItemToQueue, xTicksToWait )
    /* 往队列的头部写入消息 */
    xQueueSendToFront( xQueue, pvItemToQueue, xTicksToWait )
    /* 覆写队列消息(只用于队列长度为 1 的情况)*/
    xQueueOverwrite( xQueue, pvItemToQueue )/* 在中断中往队列的尾部写入消息 */
    xQueueSendFromISR( xQueue, pvItemToQueue, pxHigherPriorityTaskWoken )
    /* 同 xQueueSendFromISR() */
    xQueueSendToBackFromISR( xQueue, pvItemToQueue, pxHigherPriorityTaskWoken )   
    /* 在中断中往队列的头部写入消息 */
    xQueueSendToFrontFromISR( xQueue, pvItemToQueue, pxHigherPriorityTaskWoken )
    /* 在中断中覆写队列消息(只用于队列长度为 1 的情况)*/
    xQueueOverwriteFromISR( xQueue, pvItemToQueue, pxHigherPriorityTaskWoken )

    写入消息函数有点多了,只说明一下任务级的写入函数,中断级差不多,只是在中断中调用,个别参数不一样

            任务级的写入函数在本质上都是对 xQueueGenericSend() 函数的宏定义,只是由于参数不一样导致写入的情况也不同,xQueueGenericSend() 函数原型如下:

    BaseType_t xQueueGenericSend( QueueHandle_t xQueue,const void * const pvItemToQueue,TickType_t xTicksToWait,const BaseType_t xCopyPosition );

            参数说明:

    • xQueue:需要写入的队列
    • pvItemToQueue:待写入消息
    • xTicksToWait:写入阻塞等待时间,xQueueOverwrite() 函数覆写阻塞时间为 0。
    • xCopyPosition:写入的位置,上面任务级函数之间最大的区别就是写入位置封装不一样。
      • queueSEND_TO_BACK 尾部写入。

      • queueSEND_TO_FRONT 头部写入。

    • 返回值:pdTRUE 队列写入成功,errQUEUE_FULL 队列写入失败。

    上面 xQueueGenericSend() 函数的参数列表与宏函数的参数列表是对应的,如果有函数没有相关参数,那就是隐藏了,如:

            这个尾部写入并不是在队列的最末尾写入,而是队列的‌逻辑尾部‌(即FIFO顺序的末端),头部写入也是类似的。 具体可以看上面的队列写入操作。

    3.3 队列读取消息

    /* 从队列头部读取消息,并删除消息 */
    BaseType_t xQueueReceive( QueueHandle_t xQueue,void * const pvBuffer,TickType_t xTicksToWait )
    /* 从队列头部读取消息 */
    BaseType_t xQueuePeek( QueueHandle_t xQueue,void * const pvBuffer,TickType_t xTicksToWait )/* 在中断中从队列头部读取消息,并删除消息 */
    BaseType_t xQueueReceiveFromISR( QueueHandle_t xQueue,void * const pvBuffer,BaseType_t * const pxHigherPriorityTaskWoken )
    /* 在中断中从队列头部读取消息 */
    BaseType_t xQueuePeekFromISR( QueueHandle_t xQueue,void * const pvBuffer )

    上面的读取消息函数都是从队列头部开始读取的,下面说明限于任务级的。

            参数说明:

    • xQueue:待读取的队列
    • pvBuffer:信息读取缓冲区
    • xTicksToWait :阻塞超时时间
    • 返回值:pdTRUE 读取成功,pdFALSE 读取失败

     出队与入队有两种方式:先进先出(FIFO),后进先出(LIFO),就是不同读写函数的搭配使用

            先进先出(FIFO):尾部写入,头部读出。

            后进先出(LIFO):头部写入,头部读取。

    四、代码示例

    创建 4 个任务,start_task,task1,task2,task3,作用如下

    start_task创建task1,task2 ,task3这三个任务,并且创建两个队列,一个用于存储键值,一个用于存储字符串
    task1进行按键扫描,按键1 发送键值,按键2 发送字符串地址
    task2读取队列1,即存储键值的队列并打印
    task3读取队列2,读取字符串地址并打印

            代码如下:

    main.c  文件:

    #include "malloc.h"
    #include "freertos_demo.h"
    #include "sys.h"
    #include "usart.h"
    #include "delay.h"
    #include "usmart.h"
    #include "led.h"
    #include "lcd.h"
    #include "key.h"int main(void)
    {HAL_Init();                         /* 初始化HAL库 */sys_stm32_clock_init(RCC_PLL_MUL9); /* 设置时钟, 72Mhz */delay_init(72);                     /* 延时初始化 */usart_init(115200);                 /* 串口初始化为115200 */led_init();                         /* 初始化LED */lcd_init();                         /* 初始化LCD */key_init();                         /* 初始化按键 */my_mem_init(SRAMIN);                /* 初始化内部SRAM内存池 */freertos_demo();                    /* 创建起始任务 */
    }

    freertos_demo.c 文件:

    
    #include "freertos_demo.h"
    #include "string.h"
    #include "usart.h"
    #include "led.h"
    #include "lcd.h"
    #include "key.h"/*FreeRTOS******************************************************************************/
    #include "FreeRTOS.h"
    #include "task.h"
    #include "queue.h"/***************************************************************************************//* START_TASK 任务 配置* 包括: 任务句柄 任务优先级 堆栈大小 创建任务*/
    #define TASK_STACK_SIZE 256
    #define TASK_PRIORITY   1
    TaskHandle_t start_task_handle;
    void start_task(void *pvParameters);/* TASK1 任务 配置* 包括: 任务句柄 任务优先级 堆栈大小 创建任务*/
    #define TASK1_STACK_SIZE 256
    #define TASK1_PRIORITY   2
    TaskHandle_t task1_handle;
    void task1(void *pvParameters);/* TASK2 任务 配置* 包括: 任务句柄 任务优先级 堆栈大小 创建任务*/
    #define TASK2_STACK_SIZE 256
    #define TASK2_PRIORITY   3
    TaskHandle_t task2_handle;
    void task2(void *pvParameters);/* TASK3 任务 配置* 包括: 任务句柄 任务优先级 堆栈大小 创建任务*/
    #define TASK3_STACK_SIZE 256
    #define TASK3_PRIORITY   4
    TaskHandle_t task3_handle;
    void task3(void *pvParameters);/***************************************************************************************/
    QueueHandle_t key_queue;              /* 小数据队列句柄 */
    QueueHandle_t big_date_queue;         /* 大数据队列句柄 */
    char buffer[64] = "Hello World";      /* 待发送的字符串 *//* 在main函数中被调用 */
    void freertos_demo(void)
    {xTaskCreate((TaskFunction_t         )start_task,            /*函数指针,函数入口*/(char *                )"start_task",           /*任务名字*/(configSTACK_DEPTH_TYPE)TASK_STACK_SIZE,        /*任务堆栈的大小*/(void *                )NULL,                   /*任务参数*/(UBaseType_t           )TASK_PRIORITY,          /*任务优先级*/(TaskHandle_t *        )&start_task_handle      /*任务句柄*/);vTaskStartScheduler();
    }/* 创建其他任务和队列 */
    void start_task(void *pvParameters)
    {taskENTER_CRITICAL();key_queue = xQueueCreate( 2, sizeof(uint8_t) );if(key_queue != NULL){printf("小数据队列创建成功\r\n");}big_date_queue = xQueueCreate( 1, sizeof(char *) );if(big_date_queue != NULL){printf("大数据队列创建成功\r\n");}xTaskCreate((TaskFunction_t         )task1,(char *                )"task1",(configSTACK_DEPTH_TYPE)TASK1_STACK_SIZE,(void *                )NULL,(UBaseType_t           )TASK1_PRIORITY,(TaskHandle_t *        )&task1_handle);xTaskCreate((TaskFunction_t         )task2,(char *                )"task2",(configSTACK_DEPTH_TYPE)TASK2_STACK_SIZE,(void *                )NULL,(UBaseType_t           )TASK2_PRIORITY,(TaskHandle_t *        )&task2_handle);xTaskCreate((TaskFunction_t         )task3,(char *                )"task3",(configSTACK_DEPTH_TYPE)TASK3_STACK_SIZE,(void *                )NULL,(UBaseType_t           )TASK3_PRIORITY,(TaskHandle_t *        )&task3_handle);taskEXIT_CRITICAL();vTaskDelete(NULL);		/*删除当前任务*/
    }/* 任务1:读取小数据队列中的数据 */
    void task1(void *pvParameters)
    {uint8_t key_val = 0;while(1){xQueueReceive(key_queue ,&key_val,portMAX_DELAY);printf("接受的键值为:%d\r\n",key_val);key_val = 0;vTaskDelay(50);}
    }/* 任务2:读取大数据队列的数据 */
    void task2(void *pvParameters)
    {char * buf;while(1){xQueueReceive(big_date_queue,&buf,portMAX_DELAY);printf("接受的字符串为: %s\r\n",buf);vTaskDelay(50);}
    }/* 任务3:按键捕获,往消息队列中发送数据 */
    void task3(void *pvParameters)
    {uint8_t key = 0;char*  buf  = buffer;while(1){key = key_scan(0);if(key == KEY0_PRES || key == KEY1_PRES){if(pdTRUE != xQueueSend( key_queue, (void *) &key, portMAX_DELAY))printf("键值发送失败\r\n");}else if(key == WKUP_PRES){if(xQueueSend(big_date_queue,&buf,portMAX_DELAY) != pdTRUE)printf("字符串发送失败\r\n");}vTaskDelay(10);}
    }

    相关文章:

    FreeRTOS--消息队列

    一、简介 消息队列是FreeRTOS中用于任务与任务或任务与中断之间数据交换的一种机制,采用FIFO(先进先出)方式管理数据,也可以采用LIFO(后进先出)方式。有点类似全局变量。 1.1 那为什么不直接使用全局变量&a…...

    三步快速部署一个本地Windows/Linux大语言模型ChatGLM(环境配置+权重下载+运行)

    前言: 最近刚拿到实验室一个装了3张3090显卡的服务器账号,感觉不用来霍霍有点浪费,于是有了部署一个大语言模型的想法,除去下载权重和传文件到服务器上可能也就用了十分钟不到(这下看懂为啥python受众现在这么广了&…...

    深入解析Spring Boot与Redis的缓存集成实践

    深入解析Spring Boot与Redis的缓存集成实践 引言 在现代Web应用开发中,缓存技术是提升系统性能的重要手段之一。Redis作为一种高性能的内存数据库,广泛应用于缓存场景。本文将详细介绍如何在Spring Boot项目中集成Redis,并探讨其在实际开发…...

    leetcode105.从中序与前序遍历序列构造二叉树:前序定根与中序分治的递归重建术

    一、题目深度解析与核心挑战 在二叉树的重建问题中,"从中序与前序遍历序列构造二叉树"是一道考察递归分治思想的经典题目。题目要求我们根据一棵二叉树的前序遍历序列和中序遍历序列,重建出该二叉树的原始结构。这道题的核心难点在于如何利用…...

    Python二级考试

    目录 一、核心知识模块 1. 程序结构 2. 循环结构 3. 组合数据类型 4. 函数与模块 二、重点算法 1. 排序算法 2. 查找算法 三、文件操作 1. 基础文件处理 四、备考建议 五、典型易错点 以下是Python二级考试的复习要点整理,分为知识模块和备考建议&#…...

    DeepSeek联网Google搜索引擎

    目录: 1、使用背景2、实现代码3、Gradio 的 yield 机制 1、使用背景 比如所有易建联是什么时候退役的?使用大模型对这种实事回答不准确,需要通过联网搜索处理。 正确答案应该是2023年8月29日退役。 2、实现代码 # import gradio as gr# d…...

    理论物理:为什么在极低温(接近绝对零度)时,经典理论失效?

    经典理论应该是指经典力学和经典统计物理吧,比如牛顿力学、麦克斯韦-玻尔兹曼分布这些。而到了接近绝对零度的时候,物质的状态会发生什么变化呢?比如说超流性、超导性,或者玻色-爱因斯坦凝聚这些现象,这些在经典理论里好像没法解释。 因为在极低温下,粒子的热运动减弱,…...

    奈雪小程序任务脚本

    功能概述 该脚本用于自动完成奈雪点单小程序的每日任务,包括: 自动检测 Token 有效性自动签到(如果未签到)获取用户基础信息(昵称、手机号)查询当前奈雪币余额记录连续签到天数支持多账号执行&#xff0c…...

    上海医日健集团物联网专利技术领跑智慧药房赛道

    在智慧医疗蓬勃发展的浪潮中,上海医日健集团凭借其卓越的创新能力与强大的技术实力,在智慧药房领域崭露头角。集团自主研发的物联网专利技术,正以前所未有的优势,重塑智慧药房运营模式,引领行业迈向新的发展高度。 上…...

    基于Java+MySQL实现(Web)图书借阅管理系统

    图书借阅管理系统(前后台) 1 需求分析 图书借阅管理系统是模拟学校图书馆实现的一个具有前后台的 Web 系统.对于读者,能够提供全文检索,个性化推荐,借阅等功能.对于管理员,能够提供可视化数据分析,信息管理等功能. 2 技术栈 前端: Layui,jQuery,echarts 后端:Spring Boot,…...

    SAR ADC的功耗设计

    SAR ADC 由比较器、逻辑和DAC组成,功耗比可能是3:6:1,对于低功耗设计来说,我们需要尽量让DAC的功耗最小,这里来探讨一下CDAC的功耗计算方法。 CDAC从状态1切换到状态2时,需要从Vref buffer上抽拉电荷。C是状态2时连接Vref的总电容,V2就是状态2时接Vref的电容上的电压…...

    PP-OCRv5

    目录 PP-OCRv5官方效果如下 C封装、C#调用效果 项目 代码 下载 PP-OCRv5官方效果如下 C封装、C#调用效果 项目 代码 using Newtonsoft.Json; using OpenCvSharp; using System; using System.Collections.Generic; using System.Diagnostics; using System.Drawing; usi…...

    nginx的一些配置的意思

    1.用这个端口可以访问到nginx 2.工作进程,设置成和cpu核心数一样即可 3.每个工作进程的最大网络连接数。 4.主机名称 设置反向代理时,把server_name设置成ip。 5.反向代理进行转发,localhost指的是nginx所在的机器。 关键字proxy_pass。 …...

    Agent模型微调

    这篇文章讲解: 把 Agent 和 Fine-Tuning 的知识串起来,在更高的技术视角看大模型应用;加深对 Agent 工作原理的理解;加深对 Fine-Tuning 训练数据处理的理解。 1. 认识大模型 Agent 1.1 大模型 Agent 的应用场景 揭秘Agent核心…...

    Android-OkHttp与Retrofit学习总结

    OkHttp核心机制与工作流程​ ​面试官​:能简单介绍一下OkHttp的工作流程吗? ​候选人​: 好的,OkHttp的工作流程大致可以分为几个步骤。首先,我们需要创建一个OkHttpClient实例,通常会用建造者模式来配置…...

    移远三款主流5G模块RM500U,RM520N,RG200U比较

    文章目录 概要一、技术架构差异1. ‌3GPP协议版本‌2. ‌芯片平台与性能‌3. ‌频段覆盖与区域适配‌4. 协议增强与特殊功能‌ 二、功能与应用定位1. ‌网络兼容性‌2. ‌封装与接口扩展‌ 三、典型应用场景总结 概要 本文介绍下移远两款主流5G模块RM500U RM520N RG200U。 一…...

    C++引用以及和指针的区别

    C++ 引用 引用(reference)是 C++ 中的一种变量类型,是另一个变量的别名。一旦引用被初始化,就不能再改变它所指向的对象。 引用的特点 必须初始化:声明引用时必须立即对其进行初始化。不可更改绑定:一旦引用绑定到某个变量,就不能再指向其他变量。语法简洁:使用引用不…...

    firfox 国外版和国内版本账号不互通问题处理

    https://blog.csdn.net/sinat_37891718/article/details/147445621 现在国际服的火狐浏览器修改使用国内的账号服务器,需要先在搜索框输入about:config 中改变三项配置,然后重启浏览器,才能正常使用国内的火狐账号服务器 identity.fxaccount…...

    Linux基本指令篇 —— whoami指令

    whoami 是 Linux 和 Unix 系统中一个简单但实用的命令,全称 Who Am I(我是谁)。它的功能是显示当前登录用户的用户名。以下是关于 whoami 的详细解析: 目录 1. 基本用法 2. 命令特点 3. 实际应用场景 场景 1:脚本中…...

    用go从零构建写一个RPC(3)--异步调用+多路复用实现

    在前两个版本中,我们实现了基础的客户端-服务端通信、连接池、序列化等关键模块。为了进一步提升吞吐量和并发性能,本版本新增了 异步发送机制 和 多路复用支持,旨在减少资源消耗、提升连接利用率。 代码地址:https://github.com/…...

    力扣395做题笔记

    题目链接 力扣395 第一次尝试 class Solution {public int longestSubstring(String str, int k) {char[] s str.toCharArray();int n s.length;int[] cnts new int[256];int ans 0;for (int r 0, l 0; r < n; r ) { cnts[s[r]];if (cnts[s[r]] > k) { ans Mat…...

    Python-numpy中常用的统计函数及转换函数

    numpy中常用的统计函数 numpy中常用统计函数numpy普通统计函数忽略 NaN 值进行统计百分位数 numpy中形状转换函数重塑数组&#xff08;reshape&#xff09;展平数组&#xff08;flatten/ravel&#xff09;转置&#xff08;transpose/T&#xff09; 数据类型的转换使用astype()转…...

    【C语言干货】free细节

    提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言一、为啥*phead free掉了之后&#xff0c;为啥下面还 提示&#xff1a;以下是本篇文章正文内容&#xff0c;下面案例可供 可以用&#xff1f; 前言参考 一、为…...

    网络安全-等级保护(等保) 2-0 等级保护制度现行技术标准

    ################################################################################ 第二章&#xff1a;现行等保标准要求&#xff0c;通过表格方式详细拆分了等保的相关要求。 GB 17859-1999 计算机信息系统 安全保护等级划分准则【现行】 GB/T22240-2020 《信息安全技术 网…...

    WebSocket(看这一篇就够了)

    文章目录 WebSocket 基本概念什么是WebSocket?为什么需要 WebSocket&#xff1f;与 HTTP 协议的区别WebSocket协议的原理WebSocket工作流程WebSocket 数据帧结构和控制帧结构。JavaScript 中 WebSocket 对象的属性和方法&#xff0c;以及如何创建和连接 WebSocket。webSocket简…...

    旧物回收小程序:让闲置焕发光彩,为生活增添价值

    你是否常常为家中堆积如山的闲置物品而烦恼&#xff1f;那些曾经心爱的物品&#xff0c;如今却成了占据空间的“鸡肋”&#xff0c;丢弃可惜&#xff0c;留着又无处安放。别担心&#xff0c;一款旧物二手回收小程序将为你解决这一难题&#xff0c;让闲置物品重新焕发光彩&#…...

    精益数据分析(73/126):黏性阶段的功能优先级法则——七问决策模型与风险控制

    精益数据分析&#xff08;73/126&#xff09;&#xff1a;黏性阶段的功能优先级法则——七问决策模型与风险控制 在创业的黏性阶段&#xff0c;如何从海量的功能创意中筛选出真正能提升用户留存的关键改动&#xff1f;今天&#xff0c;我们结合《精益数据分析》中的“开发功能…...

    React声明式编程(手动控制,大型项目,深度定制)与Vue响应式系统(自动优化,中小型项目,快速开发)区别

    文章目录 React声明式与Vue响应式区别详解一、响应式机制原理对比1.1 Vue的响应式系统Vue响应式流程图Vue响应式代码示例 1.2 React的声明式更新React声明式流程图React声明式代码示例 二、更新触发逻辑差异2.1 Vue的自动更新Vue依赖收集机制 2.2 React的手动更新React Diff算法…...

    数学建模MathAI智能体-2025电工杯A题实战

    题目&#xff1a; 光伏电站发电功率日前预测问题 光伏发电是通过半导体材料的光电效应&#xff0c;将太阳能直接转化为电能的技术。光伏电站是由众多光伏发电单元组成的规模化发电设施。 光伏电站的发电功率主要由光伏板表面接收到的太阳辐射总量决定&#xff0c;不同季节太阳…...

    跨平台游戏引擎 Axmol-2.6.0 发布

    Axmol 2.6.0 版本是一个以错误修复和功能改进为主的次要LTS长期支持版本 &#x1f64f;感谢所有贡献者及财务赞助者&#xff1a;scorewarrior、peterkharitonov、duong、thienphuoc、bingsoo、asnagni、paulocoutinhox、DelinWorks 相对于2.5.0版本的重要变更&#xff1a; 通…...