【FreeRTOS】详细讲解FreeRTOS中消息队列并通过示例讲述其用法

news2024/9/20 5:56:04

讲解FreeRTOS中消息队列及其用法

  • 使用消息队列的原因
  • 消息队列
  • 函数解析
  • 示例
  • 遇到的问题

使用消息队列的原因

  在裸机系统中,两个程序间需要共享某个资源通常使用全局变量来实现;但在含操作系统(下文就拿FreeRTOS举例)的开发中,则使用消息队列完成。那么这两者有啥区别呢?🤔🤔🤔
  其实在FreeRTOS系统中也能够像裸机似的,使用全局变量实现多程序共享某个资源(这里资源就可称为临界资源),则多个程序都能随时访问同一个临界资源,这时若两个程序同时访问同一个临界资源来完成两次资源读写操作,假如两个程序读取操作是同时完成,但是写入操作有先后之别,那么最后实际完成的操作就会是一个。例如下图:

  看完上图后,大家可能会想:两者结果相同,无所谓了。但是呢,如果此时再来个C程序恰好读取到的值为456,那么是不是跟最终结果789存在偏差呢!!!😅😅😅

  因此,在FreeRTOS系统中,引入了消息队列来实现某个资源共享,其不仅仅实现临界资源共享,也给临界资源提供保护,使得程序更加稳定。


消息队列

  消息队列,是一种用于任务与任务间、中断和任务间传递一条或多条信息的数据结构,实现了任务接收来自其他任务中断不固定或固定长度的消息
  任务从队列里面读取消息时,如果队列中消息为空,读取消息的任务将被阻塞;否则任务就读取消息并且处理。用户还可以指定阻塞任务时间 xTicksToWait(),在指定阻塞时间内,如果队列为空,该任务将保持阻塞状态以等待队列数据有效
  有多个消息发送到消息队列时,通常将先进入队列的消息先传给任务,也就是说,任务一般读取到的消息是最先进入消息队列的消息,即先进先出原则(FIFO),但也支持后进先出原则(LIFO)
  FreeRTOS 中使用队列数据结构实现任务异步通信工作,其具有如下特性:

  • 消息支持先进先出的方式排队,支持异步读写的工作方式;
  • 读写队列均支持超时机制
  • 消息支持后进先出方式排队,即直接往队首发送消息(LIFO);
  • 允许不同长度(不超过队列节点最大值)的任意类型消息;
  • 一个任务能够与任意一个消息队列接收和发送消息的操作;
  • 多个任务能够与同一个消息队列接收和发送消息的操作;
  • 当队列使用结束后,可以通过删除队列函数进行删除函数

消息队列收发双方处理机制

  • 创建消息队列,FreeRTOS系统会分配一块单个消息大小与消息队列长度乘积的空间;(创建成功后,每个消息的大小及消息队列长度无法更改,不能写入大于单个消息大小的数据,并且只有删除消息队列时,才能释放队列占用的内存。)

  • 写入消息队列,当消息队列未满或允许覆盖入队时,FreeRTOS系统会直接将消息复制到队列末端;否则,程序会根据指定的阻塞时间进入阻塞状态,直到消息队列未满或者是阻塞时间超时,程序就会进入就绪状态;
    写入紧急消息,本质上与普通消息差不多,不同的是其将消息直接复制到消息队列队首

  • 读取消息队列,在指定阻塞时间内,未读取到消息队列中的数据(消息队列为空),程序进入阻塞状态,等待消息队列中有数据;一旦阻塞时间超时,程序进入就绪态

  • 一旦消息队列不再使用时,应该将其删除;(此时会永久删除)

消息队列的处理机制图
(图有点丑,大家伙将就一下吧🤣🤣🤣)

函数解析

消息队列通用创建
函数原型

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

参数解析

  • const UBaseType_t uxQueueLength:设置消息队列长度;
  • const UBaseType_t uxItemSize:设置消息队列中单个消息大小;
  • const uint8_t ucQueueType:设置消息队列的类型;

函数说明
  一个通用的消息队列创建函数,该函数自己给其他函数提供API,自己也调用函数prvInitialiseNewQueue()完成消息队列创建功能。

消息队列动态创建
函数原型

QueueHandle_t xQueueCreate(
                            UBaseType_t uxQueueLength,
                            UBaseType_t uxItemSize
                         );

参数解析

  • UBaseType_t uxQueueLength:设置消息队列长度;
  • UBaseType_t uxItemSize:设置消息队列中单个消息的大小;

函数说明
  创建函数,实际上使用还是调用函数 xQueueGenericCreate()完成消息队列创建工作。
  当消息队列创建成功时,返回一个消息队列的控制句柄,用于访问创建的队列;否则,返回NULL,可能原因是创建队列需要的 RAM 无法分配成功。

消息队列静态创建
函数原型

QueueHandle_t xQueueCreateStatic(
 	                             UBaseType_t uxQueueLength,
    	                         UBaseType_t uxItemSize,
        	                     uint8_t *pucQueueStorage,
            	                 StaticQueue_t *pxQueueBuffer
                		        );

参数解析

  • UBaseType_t uxQueueLength:设置消息队列长度;
  • UBaseType_t uxItemSize:设置消息队列中单个消息大小;
  • uint8_t *pucQueueStorage:传递消息队列中单个消息的存储结构;
  • StaticQueue_t *pxQueueBuffer:传递自定义的消息队列;

函数说明

  xQueueCreateStatic()用于创建一个新的队列并返回可用于访问这个队列的队列句柄,队列句柄其实就是一个指向队列数据结构类型的指针。
  当返回值为NULL时,创建失败,失败原因与动态创建类似,即可能是创建队列需要的 RAM 无法分配成功。

消息队列删除
函数原型

void vQueueDelete( QueueHandle_t xQueue );

参数解析

  • QueueHandle_t xQueue:消息队列的控制句柄;

函数说明
  使用函数vQueueDelete()可以将一个消息队列中的所有信息都清空回收,并且该队列将无法继续使用。值得注意的是,一个没有创建的消息队列,是无法删除的

发送消息到消息队列
函数原型

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

参数解析

  • QueueHandle_t xQueue:传入消息队列的控制句柄;
  • const void * pvItemToQueue:传入需要发送到消息队列中的数据;
  • TickType_t xTicksToWait:设置阻塞超时时间,设置成0,可直接返回;

函数说明
  发送函数实际上调用PAI函数xQueueGenericSend();该函数也等同于函数xQueueSendToBack()
  发送消息函数xQueueSend(),其处理机制为:当消息队列未满或允许覆盖入队时,FreeRTOS系统会直接将消息复制到队列末端;否则,程序会根据指定的阻塞时间进入阻塞状态,直到消息队列未满或者是阻塞时间超时,程序就会进入就绪状态。

中断中发送消息到消息队列
函数原型

 BaseType_t xQueueSendFromISR(
                               QueueHandle_t xQueue,
                               const void *pvItemToQueue,
                               BaseType_t *pxHigherPriorityTaskWoken
                             );

参数解析

  • QueueHandle_t xQueue:传递消息队列的控制权柄;
  • const void *pvItemToQueue:传递需要发送的消息;
  • BaseType_t *pxHigherPriorityTaskWoken:若消息入队列时产生一个更高优先级的任务,那么改参数就会被设置成pdTRUE,系统在中断函数结束前会切换任务,去执行更高优先级的任务。在FReeRTOS V7.3.0起,该函数为一个可选参数。

函数说明
  该函数实际上调用FreeRTOS系统APIxQueueGenericSendFromISR()来完成在中断中发送消息。该函数功能上与xQueueSendToBackFromISR()相同,并且两者参数完全一致。

发送消息到消息队列队首
函数原型

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

参数解析

  • QueueHandle_t xQueue:消息队列的控制句柄;
  • const void *pvItemToQueue:需要发送的消息;
  • TickType_t xTicksToWait:函数阻塞超时时间;

函数说明
  该函数实际上还是调用函数xQueueGenericSend()来完成其功能的。
  xQueueSendToToFront()向队列队首发送一个消息;发送消息成功返回pdTRUE,否则返回 errQUEUE_FULL

中断中发送消息到消息队列队首
函数原型

 BaseType_t xQueueSendToFrontFromISR(
                                       QueueHandle_t xQueue,
                                       const void *pvItemToQueue,
                                       BaseType_t *pxHigherPriorityTaskWoken
                                    );

参数解析

  • QueueHandle_t xQueue:消息队列的控制句柄;
  • const void *pvItemToQueue:需要发送的消息;
  • BaseType_t *pxHigherPriorityTaskWoken:与xQueueSendFromISR中的该参数类似,都是与队列插入数据时产生的更高优先级任务有关;

函数说明

消息队列接收函数
函数原型

BaseType_t xQueueReceive( 
						  QueueHandle_t xQueue,
                          void * const pvBuffer,
                          TickType_t xTicksToWait
                        );

参数解析

  • QueueHandle_t xQueue:消息队列的控制权柄;
  • void * const pvBuffer:指向存储消息队列数据的存储空间;
  • TickType_t xTicksToWait:消息队列接收函数的最大阻塞时间。若该函数设置为0,则函数立刻返回;

函数说明
  一旦消息队列接收成功后会返回pdTRUE;否则,返回pdFALSE。接收消息队列后会删除该消息,倘若不想删除该信息,可使用函数xQueuePeek()

在中断中接收消息队列中消息
函数原型

BaseType_t xQueueReceiveFromISR( 
							QueueHandle_t xQueue,
                            void * const pvBuffer,
                            BaseType_t * const pxHigherPriorityTaskWoken
                                );

参数解析

  • QueueHandle_t xQueue:消息队列的控制权柄;
  • void * const pvBuffer:需要发送的消息
  • BaseType_t * const pxHigherPriorityTaskWoken:任务在往队列发送信息时,如果队列满,则任务将阻塞在该队列上,若xQueueReceiveFromISR()函数碰都一个任务,则*pxHigherPriorityTaskWoken=pdTRUE;否则,其值为NULL

函数说明
  函数xQueueReceiveFromISR()是函数xQueueReceive的中断版本,功能上一样,即接收函数后,也会将该消息删除,若不想删除可使用函数 xQueuePeekFromISR。同样,函数 xQueuePeekFromISR()是函数xQueuePeek()的中断版,功能上也是一样的。


示例

示例1
  先创建两个任务,再通过消息队列实现任务间一对一通信,来完成任务2控制任务1实现依次反转LED1-LED8的状态。此时消息队列传递的是一个整型数据

//任务控制权柄
TaskHandle_t xHandleTsak[4];
//消息队列控制权柄
QueueHandle_t xMyQueueHandle;

int main(void)
{
	//存储创建任务的返回值
	BaseType_t xReturn[5] ;
	
	xMyQueueHandle = xQueueCreate(20,sizeof(uint16_t));
	
	if(xMyQueueHandle == 0)
		//点亮LED7
		changeLedStateByLocation(LED7,ON);
	
	//动态创建任务1
	xReturn[0] = xTaskCreate(
				(TaskFunction_t )queueMesageTask1,
				(const char *)"queueMesageTask1",(uint16_t)512,
				(void*)NULL,2,&xHandleTsak[0]
				);
	
	//动态创建任务2
	xReturn[1] = xTaskCreate(
				(TaskFunction_t )queueMesageTask2,
				(const char *)"queueMesageTask2",
				(uint16_t)512,(void*)NULL,1,
				&xHandleTsak[1]
				);		
	//创建成功 
	if (pdPASS == xReturn[0] == xReturn[1])
		//启动任务,开启调度 
		vTaskStartScheduler(); 
	//创建失败
	else
		//点亮LED6
		changeLedStateByLocation(LED6,ON);
}

/********************************************
* 函数功能:消息队列测试函数1
* 函数参数:无
* 函数返回值:无
********************************************/
void queueMesageTask1(void)
{
	// 定义一个接收消息的变量
	uint16_t r_queue;
	while(1)
	{
		 if( pdTRUE == xQueueReceive( xMyQueueHandle,&r_queue,portMAX_DELAY) )
			rollbackLedByLocation(r_queue);
	}
}

/********************************************
* 函数功能:消息队列测试函数2
* 函数参数:无
* 函数返回值:无
********************************************/
void queueMesageTask2(void)
{
	uint16_t data[] = {LED1,LED2,LED3,LED4,LED5,LED6,LED7,LED8};
	//保存需要发送的数据
	static uint16_t i = 0;
	//保存系统时间
	static portTickType myPreviousWakeTime;
	//保存阻塞时间
	const volatile TickType_t xDelay1500ms = pdMS_TO_TICKS( 1500UL );
	//获取当前时间
	myPreviousWakeTime = xTaskGetTickCount();
	while(1)
	{
		xQueueSend( xMyQueueHandle,&data[i],0 );
		if(++i == 8) i = 0;
		
		//非阻塞延时1.5s
		xTaskDelayUntil( &myPreviousWakeTime,xDelay1500ms );
	}
}

示例2
  使用xTaskCreate()函数创建三个任务,其中有两个发送消息任务,一个接收消息任务,以完成一个多对一消息队列的实验。
  在创建任务时,为了使得代码更加舒服,采用带参数创建FreeRTOS任务的方法,即先将函数需要用到的参数使用结构体保存,然后再通过xTaskCreate()函数的第四个参数传递给相应任务。
  相对于示例1,示例2对任务间传递的消息也进行了优化,使其传递的数据由一个整型变量改成一个结构体改变后,即有利于消息数据的扩展,同时,也方便多个多种类型消息同时传递

// 消息队列传输的数据类型
struct messageQueue{
	int id;
	char msg[100];
};

//创建任务时传递参数结构体
struct taskParameters{
	// 任务ID
	int id;
	// 绝对延时延时时间
	uint16_t delayTime;
	// LED灯位置
	uint16_t LEDLOCATION;
	// LCD显示行
	u8 lcdLine;
	// 整型参数,用于变量倍增
	int number;
};

void queueMesageTask1(void);
void queueMesageTask2(struct taskParameters* params);

//任务控制权柄
TaskHandle_t xHandleTsak[4];
//消息队列控制权柄
QueueHandle_t xMyQueueHandle;
// 任务参数 
struct taskParameters param[2] = {{0,1500,LED1,Line6,1},{1,1000,LED2,Line7,2}};
//任务名字
char*taskName[] = {"task1","task2"};

/*****************************************
* 函数功能:freertos工作函数
* 函数参数:无
* 函数返回值:无
*****************************************/
void freertosWork(void)
{
//创建两个任务 用于测试消息队列
	unsigned  int i = 0;
	
	//存储创建任务的返回值
	BaseType_t xReturn[5] ;
	
	// 创建消息队列
	xMyQueueHandle = xQueueCreate(20,sizeof(struct messageQueue));
	if(xMyQueueHandle == 0)
		//点亮LED7
		changeLedStateByLocation(LED7,ON);
	
	//动态创建任务
	for(i=0;i<2;i++)
		xReturn[i] = xTaskCreate((TaskFunction_t )queueMesageTask2,
					(const char *)taskName[i],(uint16_t)128,
					(struct taskParameters*) &param[i],1,&xHandleTsak[i]
					);
	
	//动态创建任务1
	xReturn[3] = xTaskCreate(
					(TaskFunction_t )queueMesageTask1,
					(const char *)"queueMesageTask3",
					(uint16_t)128,(void*)NULL,1,&xHandleTsak[3]
				);		
				
	LCD_DisplayStringLine(Line0,(uint8_t*)"receve:");
	LCD_DisplayStringLine(Line5,(uint8_t*)"send:");
	
	if (pdPASS == xReturn[0] == xReturn[1] == xReturn[2])
		//点亮LED6
		changeLedStateByLocation(LED6,ON);				
	else
		vTaskStartScheduler();
}

/********************************************
* 函数功能:消息队列测试函数1
* 函数参数:无
* 函数返回值:无
********************************************/
void queueMesageTask1(void)
{	
	// 定义一个接收消息的变量
	struct messageQueue r_queue;
	char temp[100];
	int j1=1;
	while(1)
	{
		//接收数据  如果数据接收成功就处理 否则就点亮LED4
		if( pdTRUE == xQueueReceive( xMyQueueHandle,&r_queue,portMAX_DELAY) )
		{
			sprintf(temp,"(%s;%s;%d;)",pcTaskGetName(xHandleTsak[r_queue.id]),r_queue.msg,r_queue.id);
			
			//任务2发送的数据
			if(r_queue.id == 0)
				LCD_DisplayStringLine(Line1,(uint8_t*)temp);
			//任务3发送的数据
			else
				LCD_DisplayStringLine(Line2,(uint8_t*)temp);
			//每次接收数据后闪烁一次LED3
			changeAllLedByStateNumber(OFF);
			changeLedStateByLocation(LED3,j1++%2);
		}
		//数据接收失败 点亮LED4
		else
		{
			changeAllLedByStateNumber(OFF);
			changeLedStateByLocation(LED4,ON);
		}
	}
}

/********************************************
* 函数功能:消息队列测试函数2
* 函数参数:无
* 函数返回值:无
********************************************/
void queueMesageTask2(struct taskParameters* params)
{
	//初始化以及保存需要发送的数据
	struct messageQueue _sData;
	struct messageQueue*sData = &_sData;
	sData->id = params->id;
	//显示需要发送的数据
	char temp[50];
	uint16_t count = 0;
	//保存系统时间
	portTickType myPreviousWakeTime;
	//保存阻塞时间
	TickType_t xDelayms = pdMS_TO_TICKS( params->delayTime );
	//获取当前时间
	myPreviousWakeTime = xTaskGetTickCount();
	while(1)
	{
		// 改变本次发送的数据
		sprintf(sData->msg,"%s%d","Sender2:",count+=params->number);
		sprintf(temp,"(%s;%d)",sData->msg,sData->id);
		LCD_DisplayStringLine(params->lcdLine,(uint8_t*)temp);
		
		//关闭所有LED灯 避免LCD带来的影响
		changeAllLedByStateNumber(0);
		//发送数据 如果发送成功就点亮一次LED1
		if( xQueueSend( xMyQueueHandle,&_sData,0 ) == pdTRUE)
			changeLedStateByLocation(params->LEDLOCATION,ON);
		
		//非阻塞延时(ms)
		xTaskDelayUntil( &myPreviousWakeTime,xDelayms );
	}
}

结果

遇到的问题

keil报错展示

报错分析
  该报错是由于结构体初始化时引起的,keil中不支持不完整定义的变量;但是可以看看小编目前使用的变量struct taskParameters param[2] = {{0,1500,LED1,Line6,1},{1,1000,LED2,Line7,2}};其一样可以啊!😢🤔因此到底什么原因小编暂时也不得而知了。

小编的解决方案
  不知道小编这样子到底算不算解决了该问题🤣🤣🤣:
  首先,小编整理了代码,将一些不必要的变量全部都删除,并且优化了代码架构,最后这个程序莫名其妙就可以使用了,没有丝毫报错与警告。🤔😅😅😅

  还有个问题是关于变量struct taskParameters param[2] = {{0,1500,LED1,Line6,1},{1,1000,LED2,Line7,2}};的,该变量最高定义为全局变量,否则程序就会跑飞;如果实在要将其定义为局部变量也行,但是需要换一种结构体初始化的方式。


  小编这里也有其他的一些相关文章,欢迎各位点击观看😉😉😉

  • 【FreeRTOS】详细讲解FreeRTOS中任务管理并通过示例讲述其用法
  • 【FreeRTOS】详细讲解FreeRTOS的软件定时器及通过示例讲述其用法

    最后 ,欢迎大家留言或私信交流,共同进步!😁😁😁

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

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

相关文章

MonekyRunner

MonekyRunner 文章目录MonekyRunner一、简介二、JDK环境变量三、配置Android SDK环境变量3.1.下载并解压&#xff1a;3.2.环境变量&#xff1a;3.3.查看MonkeyRunner&#xff1a;四、编写Python脚本五、运行脚本一、简介 MonkeyRunner是Android SDK中自带的工具之一&#xff0…

python3:基础语法、及6种基本数据类型、找到字典的下标 index、获取list中指定元素的位置索引

基础语法 源码文件以 UTF-8 编码&#xff0c;所有字符串都是 unicode 字符串 Python 3 中&#xff0c;可以用中文作为变量名&#xff0c;非 ASCII 标识符也是允许的 标识符 第一个字符必须是字母表中字母或下划线 _ 。 标识符的其他的部分由字母、数字和下划线组成。 标识…

Java---微服务---微服务保护Sentinel

微服务保护Sentinel1.初识Sentinel1.1.雪崩问题及解决方案1.1.1.雪崩问题1.1.2.超时处理1.1.3.仓壁模式1.1.4.断路器1.1.5.限流1.1.6.总结1.2.服务保护技术对比1.3.Sentinel介绍和安装1.3.1.初识Sentinel1.3.2.安装Sentinel1.4.微服务整合Sentinel2.流量控制2.1.簇点链路2.1.快…

【自然语言处理】基于sklearn-crfsuite进行命名实体识别

基于sklearn-crfsuite进行命名实体识别0. 条件随机场1. 训练数据2. 特征提取3. 训练一个CRF模型4. 评估5. 超参数优化6. 检查参数空间7. 检查在测试数据上的最优估计器8.检查分类器学到了什么东西9.检查模型权重10. 定制化11.在控制台中进行格式化参考资料本文中&#xff0c;针…

一种前端无源码定制化开发能力专利解读

背景 目前市面上一些web前端工程在打包发布之前都会进行代码混淆加密。代码混淆(Obfuscated code)是将计算机程序的代码&#xff0c;转换成一种功能上等价&#xff0c;但是难于阅读和理解的形式的行为。代码混淆可以用于程序源代码&#xff0c;也可以用于程序编译而成的中间代…

Leetcode刷题Day38-------------------动态规划

Leetcode刷题Day38-------------------动态规划 1. 理论基础 文章链接&#xff1a;https://programmercarl.com/%E5%8A%A8%E6%80%81%E8%A7%84%E5%88%92%E7%90%86%E8%AE%BA%E5%9F%BA%E7%A1%80.html视频链接&#xff1a;https://www.bilibili.com/video/BV13Q4y197Wg题目链接&a…

GBASE荣获“2022证券基金行业信息技术应用创新联盟年度优秀成员奖

日前&#xff0c;证券基金行业信息技术应用创新联盟2022年度峰会于上海成功举办&#xff0c;在会上&#xff0c;GBASE南大通用作为联盟成员单位&#xff0c;积极相应联盟号召&#xff0c;有力支撑证券信创建设&#xff0c;荣获“2022证券基金行业信息技术应用创新联盟年度优秀成…

第一章 Arm 架构概述(2023新)

第一章 启发式 Arm 架构解读 第二章 CPU微架构 第三章 系统微架构 第四章 总线微架构 第五章 监控微架构 第六章 安全微架构 第七章 虚拟化微架构 第八章 Armv9-A 架构 第九章 Armv8-M 架构 第十章 Armv8-R 架构 第十一章 Cortex-A715 解读 第十二章 Cortex-X3 解读…

图片怎么转成PDF格式?介绍三种转换思路

PDF文件作为一类办公常见格式&#xff0c;很多场合都会使用到。有时我们需要将图片转成PDF格式以方便归纳整理。图片资料怎么转成PDF呢&#xff1f;给大家介绍几个手机和电脑都可以用的方式。希望对你有帮助。方法一、用文件自带的转换功能将图片转成PDF随意打开一个PDF文件后&…

Github每日精选(第94期):免费网页在线情况监控

Upptime Upptime 是开源的正常运行时间监控和状态页面&#xff0c;完全由 GitHub Actions、Issues 和 Pages 提供支持。 Upptime 是 [GitHub Actions] 的一个非常巧妙的用法。您基本上可以根据需要获得免费的可配置正常运行时间监视器。 github 地址在这里。 特点 利用 G…

CTPN的Python实现笔记一

文章目录一、疑难代码讲解1. 文本框左上角标注置信度(1) s str(round(i[-1] * 100, 2)) %(2) cv2.putText() 函数(3) cv2.line()函数2. 文本框进行扩展操作3. 文本框进行NMS操作(1) 非极大值抑制函数def nms(dets, thresh):a. order scores.argsort()[::-1]b. xx1 np.maxim…

[oeasy]python0068_控制序列_清屏_控制输出位置_2J

光标位置 回忆上次内容 上次了解了键盘演化的过程 ESC 从 组合键到 独立按键 ESC 的目的 是进入控制序列配置控制信息 控制信息 \033[y;xH 设置光标位置\033[2J 清屏 这到底怎么控制来着&#xff1f;&#xff1f;&#xff1f;&#x1f914;现在 系统里 这些行为 是谁来实现的…

【机器学习 - 6】:梯度下降法(第一篇)

文章目录梯度下降法的理解图解极值点和最值点梯度下降法的求导运算公式推导梯度下降法的实现梯度下降法的理解 梯度下降法不是一个机器学习算法&#xff0c;既不是在做监督学习&#xff0c;也不是在做非监督学习&#xff0c;是一种基于搜索的最优化方法。 作用&#xff1a;最小…

【2319. 判断矩阵是否是一个 X 矩阵】

来源&#xff1a;力扣&#xff08;LeetCode&#xff09; 描述&#xff1a; 如果一个正方形矩阵满足下述 全部 条件&#xff0c;则称之为一个 X 矩阵 &#xff1a; 矩阵对角线上的所有元素都 不是 0 矩阵中所有其他元素都是 0 给你一个大小为 n x n 的二维整数数组 grid &a…

JVM虚拟机知识总结

什么是虚拟机&#xff1f;从字面意思上来看&#xff0c;顾名思义即使一台虚拟的计算机&#xff0c;用来执行虚拟的计算机指令&#xff0c;从大体上来看&#xff0c;虚拟机一般分为两种。一种是系统虚拟机&#xff0c;另外一种是程序虚拟机。系统虚拟机&#xff1a;代表为VMware…

微信小程序 java中医知识库百科科普

中管理员的主要功能有&#xff1a; 1.管理员输入账户登陆后台 2.个人中心&#xff1a;管理员修改密码和账户信息 3.用户管理&#xff1a;对注册的用户信息进行删除&#xff0c;查询&#xff0c;添加&#xff0c;修改 4.中医知识管理&#xff1a;对中医的知识信息进行添加&#…

什么是地址解析协议 (ARP)?

最近不想更文章了&#xff0c;药吃完了&#xff0c; 本文目录地址解析协议 &#xff08;ARP&#xff09; 含义ARP 是做什么的&#xff0c;它是如何工作的&#xff1f;地址解析协议与 DHCP 和 DNS 的关系是什么&#xff1f;它们有何不同&#xff1f;ARP 有哪些类型&#xff1f;1…

应急救护培训报名-因疫情原因,暂停开班?

应急救护培训报名背景和急救证书区别2023年一定要被培训项目报名渠道状态上海市红十字应急救护培训上海市医疗急救中心公众课程专业课程背景和急救证书区别 关于背景和急救证书区别&#xff0c;请参考&#xff0c;程序员的生命与急救 文章 2023年一定要被培训项目 由于疫情原…

Springboot+vue冷冻仓储进销存管理系统 java

端技术&#xff1a;nodejsvueelementui 前端&#xff1a;HTML5,CSS3、JavaScript、VUE 系统分为不同的层次&#xff1a;视图层&#xff08;vue页面&#xff09;&#xff0c;表现层&#xff08;控制器类&#xff09;&#xff0c;业务层&#xff08;接口类&#xff09;和持久层&a…

php宝塔搭建部署实战易优游戏竞技公司网站源码

大家好啊&#xff0c;我是测评君&#xff0c;欢迎来到web测评。 本期给大家带来一套php开发的易优游戏竞技公司网站源码&#xff0c;感兴趣的朋友可以自行下载学习。 技术架构 PHP7.2 nginx mysql5.7 JS CSS HTMLcnetos7以上 宝塔面板 文字搭建教程 下载源码&#xff…