FreeRTOS任务状态_改进播放控制 任务管理与调度 空闲任务及其钩子函数 两个Delay函数

任务状态_改进播放控制

FreeRTOS源码概述(内存管理,入口函数,数据类型和编程规范)创建任务(声光色影,使用任务参数)删除任务(使用遥控器控制音乐)-CSDN博客icon-default.png?t=O83Ahttps://blog.csdn.net/2302_79504723/article/details/143121434?spm=1001.2014.3001.5502

任务的暂停与继续(简单介绍)

在这个文章中我们简单的实现了任务的删除与创建 但是怎么才能实现任务的暂停与继续

在我们执行任务时我们有任务的准备(ready)以及任务的运行(Running)

同时也有任务阻塞(Blocked),以及任务的暂停(Suspended)

如图

暂停音乐的代码

FreeRTOS的部分代码

void StartDefaultTask(void *argument)
{
  /* USER CODE BEGIN StartDefaultTask */
  /* Infinite loop */
    uint8_t dev, data;
    int len;
	 int bRunning;
	
	TaskHandle_t xSoundTaskHandle = NULL;
	BaseType_t ret;
	
	LCD_Init();
	LCD_Clear();

	
    IRReceiver_Init();
	LCD_PrintString(0, 0, "Waiting control");

    while (1)
    {
        /* ¶ÁÈ¡ºìÍâÒ£¿ØÆ÷ */
		if (0 == IRReceiver_Read(&dev, &data))
		{		
			if (data == 0xa8) /* play */
			{
				/* ´´½¨²¥·ÅÒôÀÖµÄÈÎÎñ */
			  extern void PlayMusic(void *params);
			  if (xSoundTaskHandle == NULL){
					LCD_ClearLine(0, 0);
					LCD_PrintString(0, 0, "Create Task");
					ret = xTaskCreate(PlayMusic, "SoundTask", 128, NULL, osPriorityNormal+1, &xSoundTaskHandle);
				   bRunning=1;
			  }else{
				  if(bRunning){
					  LCD_ClearLine(0, 0);
					  LCD_PrintString(0, 0, "Suspend Task");
					  vTaskSuspend(xSoundTaskHandle);
					  PassiveBuzzer_Control(0);
					  bRunning=0;
				  }else{
					  LCD_ClearLine(0, 0);
					  LCD_PrintString(0, 0, "Resume Task");
					  vTaskResume(xSoundTaskHandle);
					  bRunning=1;
				  }
			  }
			}
			
			else if (data == 0xa2) /* power */
			{
				/* ɾ³ý²¥·ÅÒôÀÖµÄÈÎÎñ */
				if (xSoundTaskHandle != NULL)
				{
					LCD_ClearLine(0, 0);
					LCD_PrintString(0, 0, "Delete Task");
					vTaskDelete(xSoundTaskHandle);
					PassiveBuzzer_Control(0); /* Í£Ö¹·äÃùÆ÷ */
					xSoundTaskHandle = NULL;
				}
			}
		}
    }
	
  /* USER CODE END StartDefaultTask */
}

music.c的部分代码

void MUSIC_Analysis(void){
	uint16_t MusicBeatNum = ((((sizeof(Music_Lone_Brave))/2)/3)-1);
	
	uint16_t MusicSpeed = Music_Lone_Brave[0][2];
	for(uint16_t i = 1;i<=MusicBeatNum;i++){
		//BSP_Buzzer_SetFrequency(Tone_Index[Music_Lone_Brave[i][0]][Music_Lone_Brave[i][1]]);
		PassiveBuzzer_Set_Freq_Duty(Tone_Index[Music_Lone_Brave[i][0]][Music_Lone_Brave[i][1]], 50);
		//HAL_Delay(MusicSpeed/Music_Lone_Brave[i][2]);
		//mdelay(MusicSpeed/Music_Lone_Brave[i][2]);
		vTaskDelay(MusicSpeed/Music_Lone_Brave[i][2]);
	}
}
为什么不用mdelay要用vTaskDelay?

在任务状态中vTaskDelay作用是对当前的任务进行阻塞,可以使它运行其他的任务。

在本程序中music的任务优先级高于其他的所有任务,如果单纯的delay那么程序就会进入死循环。

任务管理与调度

调度

1.相同优先级的任务轮流运行

2.最高优先级的任务先运行

3.高优先级的任务未执行完,低优先级的任务无法执行

4.一旦高优先级任务就绪马上运行

5.最高优先级的任务有多个他们轮流运行

同一优先级的所有任务他们都是通过链式进行连接的

优先级

在FreeRTOS中有56的优先级 如图

在同一优先级创建三个任务(判断执行顺序)

在创建好任务,开启任务调度器,系统会自动创建一个级别为一的prvIdleTask任务

创建任务时就会创建三个TCB结构体

pxCurrentTCB(全局TCB)的使用

指向当前(最新)创建的TCB结构体

所以pxCurrentTCB指向的是第三个任务的结构体,那么在24这个优先级中先执行任务2,再执行任务1,然后执行任务2,不断轮询(原理:在创建任务时会创建一个1ms进入一次中断的中断函数Tick)

在StartDefaultTask中创建一个高于当前任务优先级的任务

在StartDefaultTask中创建一个高于当前任务优先级的任务就会立刻ready该任务以及Running该任务,在这个任务中我们会触发vTaskDelay使任务进入DelayTaskList链表中进行等待以保证其他任务的运行,在vTaskDelay期间会触发调度器通过index检测当前任务执行到哪

在此期间每过1ms就会触发一次中断(cnt++,任务的调度操作(先看DelayList中的任务是否可以恢复,然后再找第一个非空的任务启动它))

注意

如果触发中断suspend状态,就会使任务进入xSuspendTaskList链表,这个链表不会通过调度

只能手动调出

空闲任务及其钩子函数

介绍

空闲任务(Idle 任务)的作用之一:释放被删除的任务的内存。
除了上述目的之外,为什么必须要有空闲任务?一个良好的程序,它的任务都是事件驱
动的:平时大部分时间处于阻塞状态。有可能我们自己创建的所有任务都无法执行,但是调
度 器 必 须 能 找 到 一 个 可 以 运 行 的 任 务 : 所 以 , 我 们 要 提 供 空 闲 任 务 。 在 使 用
vTaskStartScheduler() 函数来创建、启动调度器时,这个函数内部会创建空闲任务:
空闲任务优先级为 0:它不能阻碍用户任务运行
空闲任务要么处于就绪态,要么处于运行态,永远不会阻塞
空闲任务的优先级为 0,这意味着一旦某个用户的任务变为就绪态,那么空闲任务马上
被切换出去,让这个用户任务运行。在这种情况下,我们说用户任务"抢占"(pre-empt)了空
闲任务,这是由调度器实现的。
要注意的是:如果使用 vTaskDelete() 来删除任务,那么你就要确保空闲任务有机会
执行,否则就无法释放被删除任务的内存。
我们可以添加一个空闲任务的钩子函数(Idle Task Hook Functions),空闲任务的循
环每执行一次,就会调用一次钩子函数。钩子函数的作用有这些:
执行一些低优先级的、后台的、需要连续执行的函数
测量系统的空闲时间:空闲任务能被执行就意味着所有的高优先级任务都停
止了,所以测量空闲任务占据的时间,就可以算出处理器占用率。
让系统进入省电模式:空闲任务能被执行就意味着没有重要的事情要做,当
然可以进入省电模式了。
空闲任务的钩子函数的限制:
不能导致空闲任务进入阻塞状态、暂停状态
如果你会使用 vTaskDelete() 来删除任务,那么钩子函数要非常高效地执
行。如果空闲任务移植卡在钩子函数里的话,它就无法释放内存

使用钩子函数的前提

FreeRTOS\Source\tasks.c 中,可以看到如下代码,所以前提就是:
把这个宏定义为 1:configUSE_IDLE_HOOK
实现 vApplicationIdleHook 函数

任务如何退出

1.自杀 vTaskDelete(NULL)

就是在任务本身进行的删除操作,但是不能是自己杀自己,这种自杀的操作只能是交给空闲任务来删除,就是在启动调度器时系统会创造一个优先级为1的任务,来进行杀人

存在问题

由于空闲任务的优先级太低,那么可能无法执行杀人的操作,这样的话任务的创造,删除不断进行就会有很多空间碎片化,那么空间就会浪费,所以我们要将这些任务中delay转化为阻塞的函数,以保证空闲任务得以执行,以保证空闲任务负责处理动态内存分配(如通过 pvPortMalloc())后释放的内存块。

以下是这句话的翻译

1. 空闲任务的优先级

  • 优先级低:空闲任务的优先级通常设置为最低,因此在有其他高优先级任务就绪时,空闲任务不会获得 CPU 的执行机会。这可能导致空闲任务无法及时运行,从而影响内存管理和资源的释放。

2. 任务的创建和删除

  • 任务创建和删除:频繁地创建和删除任务会导致系统中产生内存碎片。尤其是动态内存分配(通过 pvPortMalloc())和释放(通过 vTaskDelete())的频繁操作,会导致内存块的分散,使得内存的使用效率降低。

3. 将 Delay 转化为阻塞的函数

  • 阻塞与延迟:如果任务使用 vTaskDelay() 等延迟函数,任务会进入阻塞状态,这样在其阻塞期间,CPU 可以运行其他任务,包括空闲任务。这确实有助于确保空闲任务能够在有其他任务等待时得以执行。

  • 使用阻塞函数:将延迟操作转化为阻塞函数(如 xSemaphoreTake() 等)可以更好地控制任务的执行流。在某些情况下,使用信号量或其他同步机制,可以确保任务在等待某些条件满足时主动放弃 CPU,让空闲任务得以运行。

4. 确保空闲任务得以执行

  • 保证内存管理:空闲任务的职责包括处理动态内存的释放。确保空闲任务能够定期执行是非常重要的,因为它会释放已经删除任务占用的内存块。若空闲任务得不到执行,系统内存可能会逐渐枯竭,导致后续的内存分配失败。

5. 总结

提到的将延迟转化为阻塞函数以确保空闲任务能够执行的观点是正确的。这样做不仅可以减少内存碎片化,还可以提高系统的资源使用效率。在设计 FreeRTOS 应用程序时,合理使用任务的延迟和阻塞机制是确保系统稳定性和性能的重要策略。

2.他杀vTaskDelete(句柄)

在别的任务中删除别的任务

两个Delay函数

vTaskDelay

vTaskDelay() 是 FreeRTOS 中用于延迟任务执行的一个重要函数。它允许任务在一定时间内进入阻塞状态,释放 CPU 控制权,使得其他任务得以运行。这对于多任务环境中的资源管理和调度非常重要。下面是关于 vTaskDelay() 的详细说明:

函数原型

void vTaskDelay( TickType_t xTicksToDelay );

参数

  • xTicksToDelay: 延迟的时间,以“滴答(ticks)”为单位。这是 FreeRTOS 内部时间管理的基本单位,通常与系统节拍频率相关。

功能

  • 当调用 vTaskDelay() 时,调用该函数的任务会被放入阻塞状态,直到指定的时间结束。在此期间,调度器将允许其他就绪任务获得 CPU 时间。

使用场景

  • 周期性任务: 当任务需要在特定时间间隔内执行时,可以使用 vTaskDelay() 来创建延迟。
  • 资源控制: 在执行某些操作(如发送数据)后,任务可能需要等待一段时间以避免过度使用资源。

示例代码

以下是一个简单的示例,展示如何使用 vTaskDelay() 创建一个周期性任务:

#include "FreeRTOS.h"
#include "task.h"

void vTaskFunction(void *pvParameters) {
    for(;;) {
        // 执行某些操作
        // ...

        // 延迟 100 个滴答
        vTaskDelay(pdMS_TO_TICKS(100)); // 将毫秒转换为滴答
    }
}

int main(void) {
    xTaskCreate(vTaskFunction, "Task", configMINIMAL_STACK_SIZE, NULL, 1, NULL);
    vTaskStartScheduler(); // 启动调度器
    for(;;); // 此处不应该执行
}

注意事项

  1. 阻塞状态: 使用 vTaskDelay() 会使任务进入阻塞状态,期间不会占用 CPU 时间。这对于降低 CPU 使用率是有帮助的。

  2. 最小延迟: vTaskDelay() 的最小延迟为一个滴答,因此如果给定的时间小于一个滴答,任务将立即恢复执行。

  3. 在中断上下文中不可用: vTaskDelay() 不能在中断服务例程(ISR)中调用。需要使用其他机制,如消息队列或信号量。

总结

vTaskDelay() 是 FreeRTOS 中一个非常实用的函数,帮助开发者有效地管理任务调度和资源使用。通过合理使用延迟,开发者可以避免 CPU 过载和资源浪费,确保系统的高效运行。

xTaskDelayUntil

xTaskDelayUntil() 是 FreeRTOS 中用于实现周期性任务的函数。与 vTaskDelay() 不同,xTaskDelayUntil() 允许任务在指定的时间间隔内精确地执行。这对于需要定时运行的任务尤其有用,可以避免由于任务执行时间不确定而导致的时间偏移。

函数原型

BaseType_t xTaskDelayUntil( TickType_t *pxPreviousWakeTime, TickType_t xTimeIncrement );

参数

  • pxPreviousWakeTime: 指向存储上次唤醒时间的变量的指针。这个变量在任务首次调用 xTaskDelayUntil() 前应被初始化为当前时间。
  • xTimeIncrement: 每次调用 xTaskDelayUntil() 时要延迟的时间,单位为滴答(ticks)。

功能

  • xTaskDelayUntil() 会使任务阻塞,直到到达下一个预定的唤醒时间。这种方法可以实现准确的周期性执行,特别是在需要维持精确时间间隔的场景中。

使用场景

  • 周期性任务: 适合需要在固定时间间隔内重复执行的任务。
  • 高精度定时: 当需要保证任务之间的执行时间不变时,xTaskDelayUntil() 是更好的选择。

示例代码

以下是使用 xTaskDelayUntil() 的一个简单示例,创建一个周期性执行的任务:

#include "FreeRTOS.h"
#include "task.h"

// 定义滴答时间
#define pdMS_TO_TICKS(ms) ((ms) / portTICK_PERIOD_MS)

void vTaskFunction(void *pvParameters) {
    TickType_t xLastWakeTime;
    const TickType_t xFrequency = pdMS_TO_TICKS(100); // 每 100 毫秒执行一次

    // 初始化 xLastWakeTime
    xLastWakeTime = xTaskGetTickCount(); 

    for(;;) {
        // 执行任务
        // ...

        // 使用 xTaskDelayUntil
        xTaskDelayUntil(&xLastWakeTime, xFrequency); // 延迟直到下一个周期
    }
}

int main(void) {
    xTaskCreate(vTaskFunction, "Task", configMINIMAL_STACK_SIZE, NULL, 1, NULL);
    vTaskStartScheduler(); // 启动调度器
    for(;;); // 此处不应该执行
}

注意事项

  1. 初始化: 在第一次调用 xTaskDelayUntil() 前,必须正确初始化 pxPreviousWakeTime,通常使用 xTaskGetTickCount() 获取当前滴答计数。

  2. 精确性: xTaskDelayUntil()vTaskDelay() 更加精确,因为它根据上次唤醒时间计算下一个唤醒时间,而不是简单地将当前时间加上延迟。

  3. 不可在 ISR 中调用: 与 vTaskDelay() 一样,xTaskDelayUntil() 也不能在中断服务例程(ISR)中调用。

总结

xTaskDelayUntil() 是一个强大的工具,可以帮助开发者创建高精度的周期性任务。通过确保任务以固定的时间间隔执行,开发者可以提高系统的响应性和稳定性。这在实时系统中尤其重要,有助于维持任务执行的周期性和时间一致性。

两个Delay的区别

在 FreeRTOS 中,vTaskDelay()xTaskDelayUntil() 是用于实现任务延迟的两个不同函数。它们在用法和功能上有一些显著的区别。

1. vTaskDelay()

  • 功能: vTaskDelay() 用于将任务阻塞一段指定的时间(以滴答为单位)。

  • 参数:

    • TickType_t xTicksToDelay: 要延迟的时间,以滴答数(ticks)为单位。
  • 行为:

    • 当调用 vTaskDelay() 时,任务将被挂起,直到指定的延迟时间过去。延迟时间是从调用 vTaskDelay() 时开始计算的。
    • 因此,如果任务在某个时刻调用 vTaskDelay(),它会延迟一段时间,之后才能重新获得 CPU 控制权。
  • 不适合的场景:

    • vTaskDelay() 的主要缺点是无法保证任务之间的周期性执行,因为如果任务执行时间过长,可能会导致后续任务的延迟。

2. xTaskDelayUntil()

  • 功能: xTaskDelayUntil() 用于实现周期性任务。它可以确保任务在固定的时间间隔内重复执行。

  • 参数:

    • TickType_t *pxPreviousWakeTime: 指向存储上次唤醒时间的变量的指针。
    • TickType_t xTimeIncrement: 每次调用 xTaskDelayUntil() 时要延迟的时间(以滴答数为单位)。
  • 行为:

    • xTaskDelayUntil() 以上次唤醒时间为基础,计算下一个唤醒时间。这意味着,无论任务的执行时间是多少,它都会确保每次执行之间的时间间隔一致。
    • 这使得 xTaskDelayUntil() 特别适合于需要以固定时间间隔执行的任务。

主要区别总结

何时使用

  • 使用 vTaskDelay(): 当你需要在任务中实现一次性的延迟,而不关心下一次执行的时机时。
  • 使用 xTaskDelayUntil(): 当你需要确保任务以固定的时间间隔运行时,尤其在周期性任务和实时应用中。

代码的验证

vTaskDelay
void LcdPrintTask(void *params)
{
	struct TaskPrintInfo *pInfo = params;
	uint32_t cnt = 0;
	int len;
	BaseType_t preTime;
	preTime=xTaskGetTickCount();
	uint64_t t1,t2;
	while (1)
	{
		/* ´òÓ¡ÐÅÏ¢ */
		if (g_LCDCanUse)
		{
			g_LCDCanUse = 0;
			len = LCD_PrintString(pInfo->x, pInfo->y, pInfo->name);
			len += LCD_PrintString(len, pInfo->y, ":");
			LCD_PrintSignedVal(len, pInfo->y, cnt++);
			g_LCDCanUse = 1;
			mdelay(cnt&0x3);
		}
		t1=system_get_ns();
		//vTaskDelayUntil(&preTime,500);
		vTaskDelay(500);
		t2=system_get_ns();
		LCD_ClearLine(pInfo->x, pInfo->y+2);
		LCD_PrintSignedVal(pInfo->x, pInfo->y+2, t2-t1);
		
	}
}
vTaskDelayUntil
void LcdPrintTask(void *params)
{
	struct TaskPrintInfo *pInfo = params;
	uint32_t cnt = 0;
	int len;
	BaseType_t preTime;
	preTime=xTaskGetTickCount();
	uint64_t t1,t2;
	while (1)
	{
		/* ´òÓ¡ÐÅÏ¢ */
		if (g_LCDCanUse)
		{
			g_LCDCanUse = 0;
			len = LCD_PrintString(pInfo->x, pInfo->y, pInfo->name);
			len += LCD_PrintString(len, pInfo->y, ":");
			LCD_PrintSignedVal(len, pInfo->y, cnt++);
			g_LCDCanUse = 1;
			mdelay(cnt&0x3);
		}
		t1=system_get_ns();
		vTaskDelayUntil(&preTime,500);
		//vTaskDelay(500);
		t2=system_get_ns();
		LCD_ClearLine(pInfo->x, pInfo->y+2);
		LCD_PrintSignedVal(pInfo->x, pInfo->y+2, t2-t1);
		
	}
}

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

网络信息安全工程师证2024年如何报考?了解这几点让你轻松考证!收藏这一篇就够了

网络信息安全工程师是一种专门从事网络安全工作的职业。随着互联网的快速发展和普及&#xff0c;网络安全问题也日益突出&#xff0c;因此网络信息安全工程师的需求也越来越大。 网络信息安全工程师主要负责保护网络系统和数据的安全&#xff0c;防止黑客攻击、病毒侵入、数据泄…

2.3 塑性力学—等效应力

个人专栏—塑性力学 1.1 塑性力学基本概念 塑性力学基本概念 1.2 弹塑性材料的三杆桁架分析 弹塑性材料的三杆桁架分析 1.3 加载路径对桁架的影响 加载路径对桁架的影响 2.1 塑性力学——应力分析基本概念 应力分析基本概念 2.2 塑性力学——主应力、主方向、不变量 主应力、主…

qt生成uuid,转成int。ai回答亲测可以

// 生成一个随机的UUID QUuid uuid QUuid::createUuid(); // 将UUID转换为字符串 QString uuidStr uuid.toString(QUuid::WithoutBraces);// 计算MD5哈希值 QByteArray hash QCryptographicHash::hash(uuidStr.toUtf8(), QCryptographicHash::Md5);// 提取前8个字节并转换为…

设计模式——装饰者模式(8)

一、定义 指在不改变现有对象结构的情况下&#xff0c;动态地给该对象增加一些职责&#xff08;即增加其额外功能&#xff09;的模式。我们先来看一个快餐店的例子。快餐店有炒面、炒饭这些快餐&#xff0c;可以额外附加鸡蛋、火腿、培根这些配菜&#xff0c;当然加配菜需要额…

高翔【自动驾驶与机器人中的SLAM技术】学习笔记(十二)拓展图优化库g2o(一)框架

【转载】理解图优化&#xff0c;一步步带你看懂g2o框架 文章来源&#xff1a;理解图优化&#xff0c;一步步带你看懂g2o框架 小白&#xff1a;师兄师兄&#xff0c;最近我在看SLAM的优化算法&#xff0c;有种方法叫“图优化”&#xff0c;以前学习算法的时候还有一个优化方法…

BigFoot BigDebuffs

BigFoot BigDebuffs 大脚插件调整目标DOT图标大小&#xff0c;其目标就是让我们自己的DOT图标大一些&#xff0c;而团队其他人小一点&#xff0c;区别开。 178新版魔兽插件站-大脚插件站-178.com BigDebuffs-v41.zip 2024.10.24下载的版本 解压文件后&#xff0c;得到一堆的…

算法魅力-双指针之滑动窗口的叛逆

#1024程序员节#征文 目录 1.滑动窗口的定义 2.算法实战 2.1 长度最小的子数组 算法思路 2.2 无重复字符的最长子串 算法思路 2.3 最大连续 1 的个数 III 算法思路 哈希表的简要补充 结束语 祝大家1024程序节快乐&#xff01;&#xff01;&#xff01; 1.滑动窗口的定…

操作系统笔记(二)进程,系统调用,I/O设备

什么是进程? 一个正在执行的程序一个包含运行一个程序所需要的所有信息的容器进程的信息保存在一个进程表中( Process Table)。进程表中的每一项对应一个进程,称为进程控制块(Process control block,PCB)。 PCB信息包括: 用户ID(UID)、进程ID(PID)…

【开源免费】基于SpringBoot+Vue.JS在线视频教育平台(JAVA毕业设计)

本文项目编号 T 027 &#xff0c;文末自助获取源码 \color{red}{T027&#xff0c;文末自助获取源码} T027&#xff0c;文末自助获取源码 目录 一、系统介绍二、演示录屏三、启动教程四、功能截图五、文案资料5.1 选题背景5.2 国内外研究现状5.3 可行性分析 六、核心代码6.1 新…

黑马JavaWeb-day03

文章目录 Ajax前后端分离开发前端工程化环境准备Vue项目Vue项目开发流程 Vue组件库ElementVue路由打包部署 Ajax Ajax:Asynchronous JavaScript And XML,异步的JavaScript和XML 作用: 数据交换:通过Ajax可以给服务器发送请求,并获取服务器相应的数据异步交互:可以在不重新加载…

1971. 寻找图中是否存在路径

有一个具有 n 个顶点的 双向 图&#xff0c;其中每个顶点标记从 0 到 n - 1&#xff08;包含 0 和 n - 1&#xff09;。图中的边用一个二维整数数组 edges 表示&#xff0c;其中 edges[i] [ui, vi] 表示顶点 ui 和顶点 vi 之间的双向边。 每个顶点对由 最多一条 边连接&#x…

ShardingSphere 分库分表入门实战

分库分表 需求分析 如果我们的平台发展迅速&#xff0c;用户量激增&#xff0c;从数据库层面去思考&#xff0c;哪个表的数据会最大呢&#xff1f; 回顾一下我们的数据库设计&#xff1a; 1&#xff09;app 应用表 显然不会&#xff0c;成百上千的应用已经多&#xff0c;但…

Chrome DevTools:Console Performance 汇总篇

Chrome DevTools Chrome 开发者工具是一套 Web 开发者工具&#xff0c;直接内置于 Google Chrome 浏览器中。 开发者工具可以帮助您即时修改页面和快速诊断问题&#xff0c;最终帮助您更快地构建更好的网站。 一、开启 DevTools 右上角菜单 > 更多工具 > 开发者工具 页面…

2015-2022年《中国县城建设统计年鉴》面板数据附下载链接

2015-2022年《中国县城建设统计年鉴》面板数据 数据简介 《中国县城建设统计年鉴》是由住建部编辑的&#xff0c;旨在全面反映我国县城建设与发展状况的统计资料。该年鉴根据各省、自治区和直辖市建设行政主管部门上报的历年县城建设统计数据编辑而成&#xff0c;每年公布一次…

Vue-插槽slot

当我们封装一个组件时&#xff0c;不希望里面的内容写死&#xff0c;希望使用的时候能够自定义里面的内容&#xff0c;这时我们就需要使用到插槽 插槽是什么呢 插槽是子组件提供给父组件的一个占位符&#xff0c;用slot标签表示&#xff0c;父组件可以在这个标签填写任何模板代…

Python自动化测试:解锁高效测试的十大魔法秘诀!

在Python自动化测试领域&#xff0c;最佳实践能够帮助提升测试效率、确保测试质量&#xff0c;并促进团队间的协作。以下是Python自动化测试的十大最佳实践&#xff0c;使用Markdown格式进行展示&#xff1a; 1. 明确测试目标和范围 描述&#xff1a;在开始编写自动化测试之前&…

MCK主机加固与防漏扫的深度解析

在当今这个信息化飞速发展的时代&#xff0c;网络安全成为了企业不可忽视的重要议题。漏洞扫描&#xff0c;简称漏扫&#xff0c;是一种旨在发现计算机系统、网络或应用程序中潜在安全漏洞的技术手段。通过自动化工具&#xff0c;漏扫能够识别出系统中存在的已知漏洞&#xff0…

全面击破工程级复杂缓存难题

目录 一、走进业务中的缓存 &#xff08;一&#xff09;本地缓存 &#xff08;二&#xff09;分布式缓存 二、缓存更新模式分析 &#xff08;一&#xff09;Cache Aside Pattern&#xff08;旁路缓存模式&#xff09; 读操作流程 写操作流程 流程问题思考 问题1&#…

openpnp - 在顶部相机/底部相机高级校正完成后,需要设置裁剪所有无效像素

文章目录 openpnp - 在顶部相机/底部相机高级校正完成后&#xff0c;需要设置裁剪所有无效像素概述笔记设置后的顶部相机效果设置后的底部相机效果 备注END openpnp - 在顶部相机/底部相机高级校正完成后&#xff0c;需要设置裁剪所有无效像素 概述 用自己编译的基于openpnp-…

《PP-OCRv1》论文精读:PaddleOCR是目前SOTA级别的OCR开源技术(截止2024年10月)

PP-OCR: A Practical Ultra Lightweight OCR System论文地址PP-OCRv2: Bag of Tricks for Ultra Lightweight OCR System论文地址PP-OCRv3: More Attempts for the Improvement of Ultra Lightweight OCR System论文地址PaddleOCR Github OCR工具库 43.5K个star PP-OCRv1由百度…