FreeRTOS 消息队列
1. 队列简介
1.1 队列的概念
队列是任务到任务、任务到中断、中断到任务数据交流的一种机制(消息传递)
类似全局变量?假设有一个全局变量a = 0,现有两个任务都在写这个变量 a:
大家想象一下如果任务 1 运行一次,任务 2 运行一次,理论上 a 等于什么?等于 2。但是大家想象一下,这个 a++ 在 C 语言这里其实它内部是分成很多个操作的,我们来看一下,内部首先就把 a 给读到 cpu 的寄存器 r0 里面,然后再把 r0 进行加一,然后加完之后,再把 r0 这个值给回这个 a 这个地址,这样的话 a 就变成 1 了,如果裸机的话,肯定是没有这种任务1、任务2 去干扰你。
但是 os 的话就存在这种可能了,比如任务 1 执行到第二步这里的时候被更高优先级的任务 2 给打断了,这时候有可能出现什么情况?那么假设一下,首先 a 本来是 0,读到 r0 里面,此时 r0 = r0 + 1 = 1,此时被任务 2 打断,那打断之后代表 r0 还没给到 a;那这时候任务 2 它同样的也是这么一个操作,因为任务 2 的优先级是最高的,所以它完整的执行了这 3 步,这时候 a 它还是 0,因为任务 1 还没有执行到第三步,还没给回 a,所以执行完这 3 步以后,a = 1,然后任务 2 执行完之后又回到任务 1 的第三步,那这时候 r0 它也是 1,1 它又赋值给 a,这时候它们运行的结果 a 就等于 1。
那这样的话执行了两个任务,都执行了一次,但是它实际的效果竟然 a = 1,这样就造成一个数据的受损。
全局变量的弊端:数据无保护,导致数据不安全,当多个任务同时对该变量操作时,数据易受损。
使用队列的情况如下:
本质:临界区->关中断->关闭优先级最低的 PendSV 中断->关闭任务调度。
队列的优点:读写队列做好了保护,防止多任务同时访问冲突;我们只需要直接调用API函数即可,简单易用!
FreeRTOS基于队列, 实现了多种功能,其中包括队列集、互斥信号量、计数型信号量、二值信号量、 递归互斥信号量,因此很有必要深入了解 FreeRTOS 的队列 。
1.2 队列的特征
在队列中可以存储数量有限、大小固定的数据。队列中的每一个数据叫做“队列项目”,队列能够存储“队列项目”的最大数量称为队列的长度。
类似于数组。
注意:在创建队列时,就要指定队列长度以及队列项目的大小!
FreeRTOS 队列特点:
- 数据入队出队方式:队列通常采用 “先进先出”(FIFO) 的数据存储缓冲机制,即先入队的数据会先从队列中被读取,FreeRTOS中也可以配置为 “后进先出”(LIFO) 方式;
- 数据传递方式。FreeRTOS中队列采用实际值传递,即将数据拷贝到队列中进行传递。FreeRTOS采用拷贝数据传递,也可以传递指针,所以在传递较大的数据的时候采用指针传递。(像 ucos 是指针传递,把地址给写进去)
优点:互不干扰。
缺点:耗时比较长。
- 多任务访问。队列不属于某个任务,任何任务和中断都可以向队列发送/读取消息。
- 出队、入队阻塞。当任务向一个队列发送消息时,假设此时当队列已满无法入队,这时候就可以指定一个阻塞时间。
- 若阻塞时间为 0 :直接返回不会等待;
- 若阻塞时间为 0~port_MAX_DELAY :等待设定的阻塞时间,若在该时间内还无法入队,超时后直接返回不再等待;
- 若阻塞时间为 port_MAX_DELAY :死等,一直等到可以入队为止。出队阻塞与入队阻塞类似;
- 入队阻塞:
队列满了,此时写不进去数据;
① 将该任务的状态列表项挂载在 pxDelayedTaskList(阻塞列表);
② 将该任务的事件列表项挂载在 xTasksWaitingToSend(等待发送列表);
- 出队阻塞:
队列为空,此时读取不了数据;
①将该任务的状态列表项挂载在 pxDelayedTaskList(阻塞列表);
②将该任务的事件列表项挂载在 xTasksWaitingToReceive(等待接收列表);
问题:当多个任务写入消息给一个“满队列”时,这些任务都会进入阻塞状态,也就是说有多个任务在等待同一个队列的空间。那当队列中有空间时,哪个任务会进入就绪态?
答:
- 优先级最高的任务
- 如果大家的优先级相同,那等待时间最久的任务会进入就绪态
1.3 队列基本操作过程
这时候这个 20 它会移到队列头部。
2. 队列结构体介绍
队列实际它的内存是分为两部分,第一个就是存放结构体的内存,接着是队列里面的队列项,它用来存放数据的,我们写入数据给队列就写入到队列项里面了。
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;
上锁计数器:当锁住队列的时候,此时还是可以正常读写队列的,只不过操作不了它的等待发送和等待接收这两个列表;当解锁的时候,才会去操作这两个列表。那这个就叫队列锁。
当用于队列使用时:
typedef struct QueuePointers
{int8_t * pcTail; /* 存储区的结束地址 */int8_t * pcReadFrom; /* 最后一个读取队列的地址 */
} QueuePointers_t;
当用于互斥信号量和递归互斥信号量时 :
typedef struct SemaphoreData
{TaskHandle_t xMutexHolder; /* 互斥信号量持有者 */UBaseType_t uxRecursiveCallCount; /* 递归互斥信号量的获取计数器 */
} SemaphoreData_t;
队列结构体整体示意图:
3. 队列相关 API 函数介绍
使用队列的主要流程:创建队列->写队列->读队列。
3.1 创建队列
创建队列相关API函数介绍:
函数 | 描述 |
---|---|
xQueueCreate() | 动态方式创建队列 |
xQueueCreateStatic() | 静态方式创建队列 |
动态和静态创建队列之间的区别:
- 动态:队列所需的内存空间由 FreeRTOS 从 FreeRTOS 管理的堆中自动分配。
- 静态:创建需要用户自行分配内存。
3.1.1 动态创建队列(常用)
#define xQueueCreate( uxQueueLength, uxItemSize ) xQueueGenericCreate( ( uxQueueLength ), ( uxItemSize ), (queueQUEUE_TYPE_BASE ))
此函数用于使用动态方式创建队列,队列所需的内存空间由 FreeRTOS 从 FreeRTOS 管理的堆中自动分配 。
形参 | 描述 |
---|---|
uxQueueLength | 队列长度 |
uxItemSize | 队列项目的大小 |
返回值 | 描述 |
---|---|
NULL | 队列创建失败 |
其他值 | 队列创建成功,返回队列句柄 |
前面说 FreeRTOS 基于队列实现了多种功能,每一种功能对应一种队列类型,队列类型的 queue.h 文件中有定义:
#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 写队列
往队列写入消息API函数:
函数(任务级+中断级) | 描述 |
---|---|
xQueueSend() | 往队列的尾部写入消息 |
xQueueSendToBack() | 同 xQueueSend() |
xQueueSendToFront() | 往队列的头部写入消息 |
xQueueOverwrite() | 覆写队列消息(只用于队列长度为 1 的情况) |
xQueueSendFromISR() | 在中断中往队列的尾部写入消息 |
xQueueSendToBackFromISR() | 同 xQueueSendFromISR() |
xQueueSendToFrontFromISR() | 在中断中往队列的头部写入消息 |
xQueueOverwriteFromISR() | 在中断中覆写队列消息(只用于队列长度为 1 的情况) |
队列写入消息
#define xQueueSend( xQueue, pvItemToQueue, xTicksToWait )xQueueGenericSend( ( xQueue ), ( pvItemToQueue ), ( xTicksToWait ), queueSEND_TO_BACK )
#define xQueueSendToBack( xQueue, pvItemToQueue, xTicksToWait )xQueueGenericSend( ( xQueue ), ( pvItemToQueue ), ( xTicksToWait ), queueSEND_TO_BACK )
#define xQueueSendToFront( xQueue, pvItemToQueue, xTicksToWait )xQueueGenericSend( ( xQueue ), ( pvItemToQueue ), ( xTicksToWait ), queueSEND_TO_FRONT )
#define xQueueOverwrite( xQueue, pvItemToQueue )xQueueGenericSend( ( xQueue ), ( pvItemToQueue ), 0, queueOVERWRITE )
可以看到这几个写入函数调用的是同一个函数 xQueueGenericSend()
,只是指定了不同的写入位置!
覆写:队列长度为 1,只有一个队列项目,比如队列里有数据了,那覆写就是不管队列中有没有数据,都会再写进去覆盖,就是永远是写的进去的,所以是不会阻塞的,所以它的默认阻塞时间是 0,就直接跳过,直接继续写。
其他像尾部写入、头部写入,那这些它们都是会阻塞的,它们都是有一个阻塞时间的设置的,如果队列满了之后,它是写不进去的。那这个是覆写方式与其他方式它们之间的一个区别。
队列一共有 3 种写入位置 :
#define queueSEND_TO_BACK ( ( BaseType_t ) 0 ) /* 写入队列尾部 */
#define queueSEND_TO_FRONT ( ( BaseType_t ) 1 ) /* 写入队列头部 */
#define queueOVERWRITE ( ( BaseType_t ) 2 ) /* 覆写队列 */
注意:覆写方式写入队列,只有在队列的队列长度为 1 时,才能够使用
往队列写入消息函数入口参数解析:
BaseType_t xQueueGenericSend(QueueHandle_t xQueue,const void * const pvItemToQueue,TickType_t xTicksToWait,const BaseType_t xCopyPosition);
形参 | 描述 |
---|---|
xQueue | 待写入的队列 |
pvItemToQueue | 待写入消息 |
xTicksToWait | 阻塞超时时间 |
xCopyPosition | 写入的位置 |
返回值 | 描述 |
---|---|
pdTRUE | 队列写入成功 |
errQUEUE_FULL | 队列写入失败 |
3.3 读队列
从队列读取消息API函数:
函数(任务级+中断级) | 描述 |
---|---|
xQueueReceive() | 从队列头部读取消息,并删除消息 |
xQueuePeek() | 从队列头部读取消息 |
xQueueReceiveFromISR() | 在中断中从队列头部读取消息,并删除消息 |
xQueuePeekFromISR() | 在中断中从队列头部读取消息 |
BaseType_t xQueueReceive( QueueHandle_t xQueue, void * const pvBuffer, TickType_t xTicksToWait )
此函数用于在任务中,从队列中读取消息,并且消息读取成功后,会将消息从队列中移除。
形参 | 描述 |
---|---|
xQueue | 待读取的队列 |
pvBuffer | 信息读取缓冲区 |
xTicksToWait | 阻塞超时时间 |
返回值 | 描述 |
---|---|
pdTRUE | 读取成功 |
pdFALSE | 读取失败 |
BaseType_t xQueuePeek( QueueHandle_t xQueue, void * const pvBuffer, TickType_t xTicksToWait )
此函数用于在任务中,从队列中读取消息, 但与函数 xQueueReceive()不同,此函数在成功读取消息后,并不会移除已读取的消息!
形参 | 描述 |
---|---|
xQueue | 待读取的队列 |
pvBuffer | 信息读取缓冲区 |
xTicksToWait | 阻塞超时时间 |
返回值 | 描述 |
---|---|
pdTRUE | 读取成功 |
pdFALSE | 读取失败 |
4. 队列操作实验
实验目的:学习 FreeRTOS 的队列相关API函数的使用 ,实现队列的入队和出队操作。
实验设计:将设计四个任务:start_task、task1、task2、task3
四个任务的功能如下:
start_task:用来创建task1和task2以及task3任务
task1:当按键 key0 或 key1 按下,将键值拷贝到队列 key_queue(入队);当按键 key_up 按下,将传输大数据,这里拷贝大数据的地址到队列 big_date_queue 中。
task2:读取队列 key_queue 中的消息(出队),打印出接收到的键值。
task3:从队列 big_date_queue 读取大数据地址,通过地址访问大数据。
4.1 创建队列
#include "queue.h"QueueHandle_t key_queue; /* 小数据句柄 */
QueueHandle_t big_date_queue; /* 大数据句柄 */
char buff[100] = {"我是一个大数组,大大的数组 124214 uhsidhaksjhdklsadhsaklj"};
/*** @brief FreeRTOS例程入口函数* @param 无* @retval 无*/
void freertos_demo(void)
{ /* 队列的创建 */key_queue = xQueueCreate( 2, sizeof(uint8_t) );if(key_queue != NULL){printf("key_queue队列创建成功!!\r\n");}else printf("key_queue队列创建失败!!\r\n");big_date_queue = xQueueCreate( 1, sizeof(char *) );if(big_date_queue != NULL){printf("big_date_queue队列创建成功!!\r\n");}else printf("big_date_queue队列创建失败!!\r\n");xTaskCreate((TaskFunction_t ) start_task,(char * ) "start_task",(configSTACK_DEPTH_TYPE ) START_TASK_STACK_SIZE,(void * ) NULL,(UBaseType_t ) START_TASK_PRIO,(TaskHandle_t * ) &start_task_handler );vTaskStartScheduler();
}
4.2 任务函数实现
/* 任务一,实现入队 */
void task1( void * pvParameters )
{uint8_t key = 0;char * buf;buf = buff; /* buf = &buff[0] */BaseType_t err = 0;while(1) {key = key_scan(0);if(key == KEY0_PRES || key == KEY1_PRES){err = xQueueSend( key_queue, &key, portMAX_DELAY ); /* 写入队列、死等 */if(err != pdTRUE){printf("key_queue队列发送失败\r\n");}}else if(key == WKUP_PRES){err = xQueueSend( big_date_queue, &buf, portMAX_DELAY ); /* 写入队列、死等 */if(err != pdTRUE){printf("key_queue队列发送失败\r\n");}}vTaskDelay(10);}
}/* 任务二,小数据出队 */
void task2( void * pvParameters )
{uint8_t key = 0;BaseType_t err = 0;while(1){err = xQueueReceive(key_queue, &key, portMAX_DELAY);if(err != pdTRUE){printf("key_queue队列读取失败\r\n");}else {printf("key_queue读取队列成功,数据:%d\r\n",key);}}
}/* 任务三,大数据出队 */
void task3( void * pvParameters )
{char * buf;BaseType_t err = 0;while(1){err = xQueueReceive(big_date_queue, &buf, portMAX_DELAY);if(err != pdTRUE){printf("big_date_queue队列读取失败\r\n");}else {printf("数据:%s\r\n",buf);}}
}
5. 队列相关API函数解析
- 队列的创建API函数:xQueueCreate( )
- 往队列写入数据API函数(入队):xQueueSend( )
- 从队列读取数据API函数(出队): xQueueReceive( )
更多的队列相关API函数讲解可以查看《FreeRTOS开发指南》第十三章—队列
相关文章:

FreeRTOS 消息队列
1. 队列简介 1.1 队列的概念 队列是任务到任务、任务到中断、中断到任务数据交流的一种机制(消息传递) 类似全局变量?假设有一个全局变量a 0,现有两个任务都在写这个变量 a: 大家想象一下如果任务 1 运行一次&#…...
如何在Python中实现列表推导式?并给出一个例子
如何在Python中实现列表推导式?并给出一个例子 Python的列表推导式(List Comprehension)是一种强大且简洁的创建列表的方法。它允许我们在一行代码中完成循环和条件判断,从而生成所需的列表。列表推导式不仅提高了代码的可读性&a…...

Flask中的Blueprints:模块化和组织大型Web应用【第142篇—Web应用】
👽发现宝藏 前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家。【点击进入巨牛的人工智能学习网站】。 Flask中的Blueprints:模块化和组织大型Web应用 在构建大型Web应用时࿰…...

如何通过idea搭建一个SpringBoot的Web项目(最基础版)
通过idea搭建一个SpringBoot的Web项目 文章目录 通过idea搭建一个SpringBoot的Web项目一、打开idea,找到 create new project二、创建方式三、配置项目依赖四、新建项目模块五、总结 一、打开idea,找到 create new project 方式1 方式2 二、创建方式 新…...
Python和FastAPI语义分析和文本图像
要点 使用FastAPI开发RESTful API,创建端点,自定义响应,结构化多路由。Pydantic数据验证库数据建模,创建依赖项注入。开发数据库和对象关系映射,SQLAlchemy,Tortoise ORM,MongoDB。建立权限和安…...
centos系统ssh7.4升级9.6
编译安装 OpenSSL 下载 OpenSSL 源代码: wget https://www.openssl.org/source/openssl-1.1.1w.tar.gz这个命令从 OpenSSL 的官方网站下载指定版本(1.1.1w)的源代码压缩包。 解压源代码: tar zxvf openssl-1.1.1w.tar.gz使用 tar…...

excel所有知识点
1要加双引号 工作表(.xlsx) 单击右键→插入,删除,移动、重命名、复制、设置标签颜色,选定全部工作表 工作表的移动:两个表打开→右键→移动(如果右键是灰色的,可能是保护工作表了)…...

显卡基础知识及元器件原理分析
显卡应该算是是目前最为火热的研发方向了,其中的明星公司当属英伟达。 当地时间8月23日,英伟达发布截至7月30日的2024财年第二财季财报,营收和利润成倍增长,均超市场预期。 财报显示,第二财季英伟达营收为135.07 亿美…...

Spark Rebalance hint的倾斜的处理(OptimizeSkewInRebalancePartitions)
背景 本文基于Spark 3.5.0 目前公司在做小文件合并的时候用到了 Spark Rebalance 这个算子,这个算子的主要作用是在AQE阶段的最后写文件的阶段进行小文件的合并,使得最后落盘的文件不会太大也不会太小,从而达到小文件合并的作用,…...

Vue 3中实现基于角色的权限认证实现思路
一、基于角色的权限认证主要步骤 在Vue 3中实现基于角色的权限认证通常涉及以下几个主要步骤: 定义角色和权限:首先需要在后端服务定义不同的角色和它们对应的权限。权限可以是对特定资源的访问权限,比如读取、写入、修改等。用户认证&#…...

Visual Studio 2022进行文件差异比较
前言 Visual Studio 2022在版本17.7.4中发布在解决方案资源管理器中比较文件的功能,通过使用此功能,可以轻松地查看两个文件之间的差异,包括添加、删除和修改的代码行。可以逐行查看差异,并根据需要手动调整和编辑文件内容以进行…...

1.2 编译型语言和解释型语言的区别
编译型语言和解释型语言的区别 通过高级语言编写的源码,我们能够轻松理解,但对于计算机来说,它只认识二进制指令,源码就是天书,根本无法识别。源码要想执行,必须先转换成二进制指令。 所谓二进制指令&…...
C语言-常量
什么是常量? 答:常量是在程序执行过程中,其值不发生改变的量,常量分为直接常量和符号常量两种。 其中直接常量又可以分为整型常量、实型常量、字符型常量、字符串常量。 直接常量 1.整型常量 整型常量即整数,包括正整数,负整数和0。c语言中常量可以用八进制,十进制和十六…...

开源的OCR工具基本使用:PaddleOCR/Tesseract/CnOCR
前言 因项目需要,调研了一下目前市面上一些开源的OCR工具,支持本地部署,非调用API,主要有PaddleOCR/CnOCR/chinese_lite OCR/EasyOCR/Tesseract/chineseocr/mmocr这几款产品。 本文主要尝试了EasyOCR/CnOCR/Tesseract/PaddleOCR这…...

vue3实现输入框短信验证码功能---全网始祖
组件功能分析 1.按键删除,清空当前input,并跳转prevInput & 获取焦点,按键delete,清空当前input,并跳转nextInput & 获取焦点。按键Home/End键,焦点跳转first/最后一个input输入框。ArrowLeft/ArrowRight键点击…...

[C#]winformYOLO区域检测任意形状区域绘制射线算法实现
【简单介绍】 Winform OpenCVSharp YOLO区域检测与任意形状区域射线绘制算法实现 在现代安全监控系统中,区域检测是一项至关重要的功能。通过使用Winform结合OpenCVSharp库,并结合YOLO(You Only Look Once)算法,我们…...

个人网站制作 Part 14 添加网站分析工具 | Web开发项目
文章目录 👩💻 基础Web开发练手项目系列:个人网站制作🚀 添加网站分析工具🔨使用Google Analytics🔧步骤 1: 注册Google Analytics账户🔧步骤 2: 获取跟踪代码 🔨使用Vue.js&#…...
数据按设定单位(分辨率)划分的方法
1. 问题描述 需要将使用公式计算后的float数值换算到固定间隔数轴的对应位置上的数据,比如2.186这个数据,将该数据换算到以0.25为间隔的数轴上,换算后是2.0,还是2.25呢?该方法就是解决这个问题。 2. 方法 输入&…...

Ubuntu 搭建gitlab服务器,及使用repo管理
一、GitLab安装与配置 GitLab 是一个用于仓库管理系统的开源项目,使用Git作为代码管理工具,并在此基础上搭建起来的Web服务。 1、安装Ubuntu系统(这个教程很多,就不展开了)。 2、安装gitlab社区版本,有需…...
QT(19)-QNetworkRequest
attribute(QNetworkRequest::Attribute code, const QVariant &defaultValue QVariant()) const 获取指定的请求属性。如果该属性未设置,则返回默认值。 hasRawHeader(const QByteArray &headerName) const 检查是否存在指定名称的原始请求头。 header(Q…...

【JavaEE】-- HTTP
1. HTTP是什么? HTTP(全称为"超文本传输协议")是一种应用非常广泛的应用层协议,HTTP是基于TCP协议的一种应用层协议。 应用层协议:是计算机网络协议栈中最高层的协议,它定义了运行在不同主机上…...
《Playwright:微软的自动化测试工具详解》
Playwright 简介:声明内容来自网络,将内容拼接整理出来的文档 Playwright 是微软开发的自动化测试工具,支持 Chrome、Firefox、Safari 等主流浏览器,提供多语言 API(Python、JavaScript、Java、.NET)。它的特点包括&a…...

1.3 VSCode安装与环境配置
进入网址Visual Studio Code - Code Editing. Redefined下载.deb文件,然后打开终端,进入下载文件夹,键入命令 sudo dpkg -i code_1.100.3-1748872405_amd64.deb 在终端键入命令code即启动vscode 需要安装插件列表 1.Chinese简化 2.ros …...
Neo4j 集群管理:原理、技术与最佳实践深度解析
Neo4j 的集群技术是其企业级高可用性、可扩展性和容错能力的核心。通过深入分析官方文档,本文将系统阐述其集群管理的核心原理、关键技术、实用技巧和行业最佳实践。 Neo4j 的 Causal Clustering 架构提供了一个强大而灵活的基石,用于构建高可用、可扩展且一致的图数据库服务…...

自然语言处理——Transformer
自然语言处理——Transformer 自注意力机制多头注意力机制Transformer 虽然循环神经网络可以对具有序列特性的数据非常有效,它能挖掘数据中的时序信息以及语义信息,但是它有一个很大的缺陷——很难并行化。 我们可以考虑用CNN来替代RNN,但是…...
爬虫基础学习day2
# 爬虫设计领域 工商:企查查、天眼查短视频:抖音、快手、西瓜 ---> 飞瓜电商:京东、淘宝、聚美优品、亚马逊 ---> 分析店铺经营决策标题、排名航空:抓取所有航空公司价格 ---> 去哪儿自媒体:采集自媒体数据进…...

网络编程(UDP编程)
思维导图 UDP基础编程(单播) 1.流程图 服务器:短信的接收方 创建套接字 (socket)-----------------------------------------》有手机指定网络信息-----------------------------------------------》有号码绑定套接字 (bind)--------------…...
Web中间件--tomcat学习
Web中间件–tomcat Java虚拟机详解 什么是JAVA虚拟机 Java虚拟机是一个抽象的计算机,它可以执行Java字节码。Java虚拟机是Java平台的一部分,Java平台由Java语言、Java API和Java虚拟机组成。Java虚拟机的主要作用是将Java字节码转换为机器代码&#x…...
绕过 Xcode?使用 Appuploader和主流工具实现 iOS 上架自动化
iOS 应用的发布流程一直是开发链路中最“苹果味”的环节:强依赖 Xcode、必须使用 macOS、各种证书和描述文件配置……对很多跨平台开发者来说,这一套流程并不友好。 特别是当你的项目主要在 Windows 或 Linux 下开发(例如 Flutter、React Na…...
Java 与 MySQL 性能优化:MySQL 慢 SQL 诊断与分析方法详解
文章目录 一、开启慢查询日志,定位耗时SQL1.1 查看慢查询日志是否开启1.2 临时开启慢查询日志1.3 永久开启慢查询日志1.4 分析慢查询日志 二、使用EXPLAIN分析SQL执行计划2.1 EXPLAIN的基本使用2.2 EXPLAIN分析案例2.3 根据EXPLAIN结果优化SQL 三、使用SHOW PROFILE…...