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

FreeRTOS入门--任务

目录

一、什么是任务

二、创建任务---xTaskCreate函数

三、任务的删除

 四、任务优先级

 1.阻塞状态(Blocked)

2.暂停状态(Suspended)

3.就绪状态(Ready)

五、Delay

六、调度算法 


一、什么是任务

在FreeRTOS中,任务就是一个函数,原型如下:

void ATaskFunction( void *pvParameters );

要注意的是:
        这个函数不能返回
        同一个函数,可以用来创建多个任务;换句话说,多个任务可以运行同一个函数

        函数内部,尽量使用局部变量:
        每个任务都有自己的栈
        每个任务运行这个函数时
        任务A的局部变量放在任务A的栈里、任务B的局部变量放在任务B的栈里
        不同任务的局部变量,有自己的副本

        任务示例如下:

void ATaskFunction( void *pvParameters )
{
/* 对于不同的任务,局部变量放在任务的栈里,有各自的副本 */
int32_t lVariableExample = 0;
/* 任务函数通常实现为一个无限循环 */
for( ;; )
{
/* 任务的代码 */
}
/* 如果程序从循环中退出,一定要使用vTaskDelete删除自己
* NULL表示删除的是自己
*/
vTaskDelete( NULL );
/* 程序不会执行到这里, 如果执行到这里就出错了 */
}

二、创建任务---xTaskCreate函数

BaseType_t xTaskCreate( TaskFunction_t pxTaskCode, // 函数指针, 任务函数
const char * const pcName, // 任务的名字
const configSTACK_DEPTH_TYPE usStackDepth, // 栈大小,单位为word,10表示40字节
void * const pvParameters, // 调用任务函数时传入的参数
UBaseType_t uxPriority, // 优先级
TaskHandle_t * const pxCreatedTask ); // 任务句柄, 以后使用它来操作这个任务

参数描述:
pvTaskCode
        函数指针,可以简单地认为任务就是一个C函数。
        它稍微特殊一点:永远不退出,或者退出时要调用"vTaskDelete(NULL)"
pcName
        任务的名字,FreeRTOS内部不使用它,仅仅起调试作用。
        长度为:configMAX_TASK_NAME_LEN
usStackDepth
        每个任务都有自己的栈,这里指定栈大小。
        单位是word,比如传入100,表示栈大小为100 word,也就是400字节。
        最大值为uint16_t的最大值。
        怎么确定栈的大小,并不容易,很多时候是估计。
        精确的办法是看反汇编码。
pvParameters

        调用pvTaskCode函数指针时用到:pvTaskCode(pvParameters)
uxPriority
        优先级范围:0~(configMAX_PRIORITIES – 1)
        数值越小优先级越低,:更高优先级的、或者后面创建的任务先运行。
        如果传入过大的值,xTaskCreate会把它调整为(configMAX_PRIORITIES – 1)
pxCreatedTask
        用来保存xTaskCreate的输出结果:task handle。
        以后如果想操作这个任务,比如修改它的优先级,就需要这个handle。
        如果不想使用该handle,可以传入NULL。
返回值
        成功:pdPASS;
        失败:errCOULD_NOT_ALLOCATE_REQUIRED_MEMORY(失败原因只有内存不足)
        注意:返回值是pdFAIL不对。
        pdFAIL是0,errCOULD_NOT_ALLOCATE_REQUIRED_MEMORY是-1。

多个任务可以使用同一个函数;

void vTaskFunction( void *pvParameters )
{const char *pcTaskText = pvParameters;volatile uint32_t ul; /* volatile用来避免被优化掉 *//* 任务函数的主体一般都是无限循环 */for( ;; ){/* 打印任务的信息 */printf(pcTaskText);/* 延迟一会(比较简单粗暴) */for( ul = 0; ul < mainDELAY_LOOP_COUNT; ul++ ){}}
}
static const char *pcTextForTask1 = "T1 run\r\n";
static const char *pcTextForTask2 = "T2 run\r\n";
int main( void )
{prvSetupHardware();xTaskCreate(vTaskFunction, "Task 1", 1000, (void *)pcTextForTask1, 1, NULL);xTaskCreate(vTaskFunction, "Task 2", 1000, (void *)pcTextForTask2, 1, NULL);/* 启动调度器 */vTaskStartScheduler();/* 如果程序运行到了这里就表示出错了, 一般是内存不足 */return 0;
}

三、任务的删除

        删除任务时使用的函数如下:

void vTaskDelete( TaskHandle_t xTaskToDelete );

pvTaskCode
        任务句柄,使用xTaskCreate创建任务时可以得到一个句柄。
        也可传入NULL,这表示删除自己。
        自杀: vTaskDelete(NULL)
        被杀:别的任务执行vTaskDelete(pvTaskCode) ,pvTaskCode是自己的句柄
        杀人:执行vTaskDelete(pvTaskCode) ,pvTaskCode是别的任务的句柄

FreeRTOS一天一个小知识之任务延时函数vTaskDelay-CSDN博客icon-default.png?t=N7T8https://blog.csdn.net/simplemethane/article/details/116998825以下是这篇文章中谈到的延迟的内容:

        Dealy的延时,是通过CPU做循环的方式来延时,CPU在延时中是做不了其他东西的,大大浪费了CPU的效率!而且非常危险!

        所以大家在裸机中如果要需要很长时间延时的话,建议用定时器来延时。

void vTaskDelay( const TickType_t xTicksToDelay ){BaseType_t xAlreadyYielded = pdFALSE;/* A delay time of zero just forces a reschedule. */if( xTicksToDelay > ( TickType_t ) 0U ){configASSERT( uxSchedulerSuspended == 0 );vTaskSuspendAll();{traceTASK_DELAY();/* A task that is removed from the event list while the* scheduler is suspended will not get placed in the ready* list or removed from the blocked list until the scheduler* is resumed.** This task cannot be in an event list as it is the currently* executing task. */prvAddCurrentTaskToDelayedList( xTicksToDelay, pdFALSE );}xAlreadyYielded = xTaskResumeAll();}else{mtCOVERAGE_TEST_MARKER();}/* Force a reschedule if xTaskResumeAll has not already done so, we may* have put ourselves to sleep. */if( xAlreadyYielded == pdFALSE ){portYIELD_WITHIN_API();}else{mtCOVERAGE_TEST_MARKER();}}

FreeRTOS这个任务执行是这样的。首先TASK1创建,然后在创建TASK2

        TASK先执行, 执行到GPIO_SetBits(GPIOC,GPIO_Pin_2);    下一句vTaskDelay(500);   延时500ms,其实就是任务挂起500ms,CPU此时不会执行TASK的任务,去执行处于就绪态的TASK2,   当TASK2的GPIO_ResetBits(GPIOC,GPIO_Pin_3);    执行好了之后执行下一条 vTaskDelay(200);此时TASK1延时500ms,TASK延时200ms。

        这时候FreeRTOS是没有执行处于就绪态的任务的,只有执行空闲任务 。此时由于TASK2是延时200ms,比TASK2延时的500ms要快,所以TASK2比TASK1更早进入就绪态,此时CPU执行  GPIO_SetBits(GPIOC,GPIO_Pin_3);    这一语句,执行好了之后TASK2又延时800ms,进入挂起态。当TASK1延时500ms到,TASK1进入就绪态,

        执行GPIO_ResetBits(GPIOC,GPIO_Pin_2);     i++;语句,执行完之后,TASK1又进入500ms的延时,进入挂起态~

任务堆栈

        任务堆栈用来保存任务现场(CPU寄存器值),创建任务的时候需要指定任务堆栈,任务堆栈的变量类型为StackType_t,再次运行任务时会从上次中断的地方开始运行

        所以在FreeRTOS中的延时函数,只是任务挂起和任务恢复而已

//任务一
void vTask1( void *pvParameters )
{
const TickType_t xDelay100ms = pdMS_TO_TICKS( 100UL );
BaseType_t ret;
/* 任务函数的主体一般都是无限循环 */
for( ;; )
{
/* 打印任务的信息 */
printf("Task1 is running\r\n");
ret = xTaskCreate( vTask2, "Task 2", 1000, NULL, 2, &xTask2Handle );
if (ret != pdPASS)
printf("Create Task2 Failed\r\n");
// 如果不休眠的话, Idle任务无法得到执行
// Idel任务会清理任务2使用的内存
// 如果不休眠则Idle任务无法执行, 最后内存耗尽
vTaskDelay( xDelay100ms );
}//任务二
void vTask2( void *pvParameters )
{
/* 打印任务的信息 */
printf("Task2 is running and about to delete itself\r\n");
// 可以直接传入参数NULL, 这里只是为了演示函数用法
vTaskDelete(xTask2Handle);
}//main函数
int main( void )
{
prvSetupHardware();
xTaskCreate(vTask1, "Task 1", 1000, NULL, 1, NULL);
/* 启动调度器 */
vTaskStartScheduler();
/* 如果程序运行到了这里就表示出错了, 一般是内存不足 */
return 0;
}

main函数中创建任务1,优先级为1。任务1运行时,它创建任务2,任务2的优先级是2。
任务2的优先级最高,它马上执行。
任务2打印一句话后,就删除了自己。
任务2被删除后,任务1的优先级最高,轮到任务1继续运行,它调用vTaskDelay() 进入Block状
态
任务1 Block期间,轮到Idle任务执行:它释放任务2的内存(TCB、栈)
时间到后,任务1变为最高优先级的任务继续执行。
如此循环。

        在任务1的函数中,如果不调用vTaskDelay,则Idle任务用于没有机会执行,它就无法释放创建任务2是分配的内存。而任务1在不断地创建任务,不断地消耗内存,最终内存耗尽再也无法创建新的任务。

 四、任务优先级

        高优先级的任务先运行。
        优先级的取值范围是:0~(configMAX_PRIORITIES – 1),数值越大优先级越高。

        FreeRTOS会确保最高优先级的、可运行的任务,马上就能执行
        对于相同优先级的、可运行的任务,轮流执行
举例子:
        厨房着火了,当然优先灭火
        喂饭、回复信息同样重要,轮流做

对于同优先级的任务,它们“轮流”执行。怎么轮流?你执行一会,我执行一会。
        "一会"怎么定义?
        人有心跳,心跳间隔基本恒定。
        FreeRTOS中也有心跳,它使用定时器产生固定间隔的中断。这叫Tick、滴答,比如每10ms发生一次时钟中断。

假设t1、t2、t3发生时钟中断
        两次中断之间的时间被称为时间片(time slice、tick period)
        时间片的长度由configTICK_RATE_HZ 决定,假设configTICK_RATE_HZ为100,那么时间片长度就是10ms

相同优先级的任务怎么切换呢?请看下图:
        任务2从t1执行到t2
        在t2发生tick中断,进入tick中断处理函数:
        选择下一个要运行的任务
                执行完中断处理函数后,切换到新的任务:任务1
                任务1从t2执行到t3
        从图中可以看出,任务运行的时间并不是严格从t1,t2,t3哪里开始

        在FreeRTOS中,系统时钟节拍的特点就是周期性中断,既然要产生中断那就需要定时器,所以在这里就是使用了一个24位的定时器,采用向下计数的方式,然后可以产生周期性的中断。
            系统在使用的时候,一般是在FreeRTOSConfig.h里面进行配置
                #define configTICK_RATE_HZ ( ( TickType_t ) 1000 )
            这是一个常用的配置,系统的节拍频率设置为1000,也就是说系统的节拍周期为1ms,这也是最为典型的一种设置。

vTaskDelay(2); // 等待2个Tick,假设configTICK_RATE_HZ=100, Tick周期时10ms, 等待20ms
// 还可以使用pdMS_TO_TICKS宏把ms转换为tick
vTaskDelay(pdMS_TO_TICKS(100)); // 等待100ms

        注意,基于Tick实现的延时并不精确,比如vTaskDelay(2) 的本意是延迟2个Tick周期,有可能经过1个Tick多一点就返回了。

        使用vTaskDelay函数时,建议以ms为单位,使用pdMS_TO_TICKS把时间转换为Tick。

        将毫秒数换算成了tick数
          #define pdMS_TO_TICKS( xTimeInMs )    ( ( TickType_t ) ( ( ( TickType_t ) ( xTimeInMs ) * ( TickType_t ) configTICK_RATE_HZ ) / ( TickType_t ) 1000U ) )

        SysTick 定时器被捆绑在 NVIC 中,用于产生 SysTick 异常(异常号: 15), 滴答定时器是一个 24 位的递减计数器,支持中断。 
            使用比较简单, 专门用于给操作系统提供时钟节拍。
            FreeRTOS 的系统时钟节拍可以在配置文件 FreeRTOSConfig.h 里面设置:
                #define configTICK_RATE_HZ ( ( TickType_t ) 1000 )
            如上所示的宏定义配置表示系统时钟节拍是 1KHz,即 1ms。

void vTask3( void *pvParameters )
{
const TickType_t xDelay3000ms = pdMS_TO_TICKS( 3000UL );
/* 任务函数的主体一般都是无限循环 */
for( ;; )
{
/* 打印任务的信息 */
printf("T3\r\n");
// 如果不休眠的话, 其他任务无法得到执行
vTaskDelay( xDelay3000ms );
}
}

修改优先级:

使用uxTaskPriorityGet来获得任务的优先级:

UBaseType_t uxTaskPriorityGet( const TaskHandle_t xTask );

使用参数xTask来指定任务,设置为NULL表示获取自己的优先级。
使用vTaskPrioritySet 来设置任务的优先级:

void vTaskPrioritySet( TaskHandle_t xTask,
UBaseType_t uxNewPriority );

使用参数xTask来指定任务,设置为NULL表示设置自己的优先级;
参数uxNewPriority表示新的优先级,取值范围是0~(configMAX_PRIORITIES – 1)。

五、任务状态

void vTask1( void *pvParameters )
{
/* 任务函数的主体一般都是无限循环 */
for( ;; )
{
/* 打印任务的信息 */
printf("T1\r\n");
}
}
void vTask2( void *pvParameters )
{
/* 任务函数的主体一般都是无限循环 */
for( ;; )
{
/* 打印任务的信息 */
printf("T2\r\n");
}
}
void vTask3( void *pvParameters )
{
const TickType_t xDelay3000ms = pdMS_TO_TICKS( 3000UL );
/* 任务函数的主体一般都是无限循环 */
for( ;; )
{
/* 打印任务的信息 */
printf("T3\r\n");
// 如果不休眠的话, 其他任务无法得到执行
vTaskDelay( xDelay3000ms );
}
}
/***********************************************/
//main函数:
int main()
{
prvSetupHardware();
xTaskCreate(vTask1, "Task 1", 1000, NULL, 1, NULL);
xTaskCreate(vTask2, "Task 2", 1000, NULL, 1, NULL);
xTaskCreate(vTask3, "Task 3", 1000, NULL, 2, NULL);
/* 启动调度器 */
vTaskStartScheduler();
/* 如果程序运行到了这里就表示出错了, 一般是内存不足 */
return 0;
}

        如果把任务3中的vTaskDelay调用注释掉,那么任务1、任务2根本没有执行的机会,任务1、任务2被"饿死"了(starve)。

 1.阻塞状态(Blocked)

在实际产品中,我们不会让一个任务一直运行,而是使用"事件驱动"的方法让它运行:任务要等待某个事件,事件发生后它才能运行在等待事件过程中,它不消耗CPU资源在等待事件的过程中,这个任务就处于阻塞状态(Blocked)
在阻塞状态的任务,它可以等待两种类型的事件:
时间相关的事件可以等待一段时间:我等2分钟也可以一直等待,直到某个绝对时间:我等到下午3点
同步事件:这事件由别的任务,或者是中断程序产生例子1:任务A等待任务B给它发送数据例子2:任务A等待用户按下按键

在等待一个同步事件时,可以加上超时时间。

        比如等待队里数据,超时时间设为10ms:
        10ms之内有数据到来:成功返回
        10ms到了,还是没有数据:超时返回

2.暂停状态(Suspended)

在日常生活的例子中,母亲正在电脑前跟同事沟通,母亲可以暂停:
        好烦啊,我暂停一会
        领导说:你暂停一下
FreeRTOS中的任务也可以进入暂停状态,唯一的方法是通过vTaskSuspend函数。函数原型如下:

void vTaskSuspend( TaskHandle_t xTaskToSuspend );

        参数xTaskToSuspend表示要暂停的任务,如果为NULL,表示暂停自己。
        要退出暂停状态,只能由别人来操作:
                别的任务调用:vTaskResume
                中断程序调用:xTaskResumeFromISR
        实际开发中,暂停状态用得不多。

3.就绪状态(Ready)

        这个任务完全准备好了,随时可以运行:只是还轮不到它。这时,它就处于就绪态(Ready)。

五、Delay

有两个Delay函数:
        vTaskDelay:至少等待指定个数的Tick Interrupt才能变为就绪状态
        vTaskDelayUntil:等待到指定的绝对时刻,才能变为就绪态。

void vTaskDelay( const TickType_t xTicksToDelay ); /* xTicksToDelay: 等待多少给
Tick */
/* pxPreviousWakeTime: 上一次被唤醒的时间
* xTimeIncrement: 要阻塞到(pxPreviousWakeTime + xTimeIncrement)
* 单位都是Tick Count
*/
BaseType_t xTaskDelayUntil( TickType_t * const pxPreviousWakeTime,
const TickType_t xTimeIncrement );

        使用vTaskDelay(n)时,进入、退出vTaskDelay的时间间隔至少是n个Tick中断
        使用xTaskDelayUntil(&Pre, n)时,前后两次退出xTaskDelayUntil的时间至少是n个Tick中断退出xTaskDelayUntil时任务就进入的就绪状态,一般都能得到执行机会,所以可以使用xTaskDelayUntil来让任务周期性地运行

int main( void )
{
prvSetupHardware();
/* Task1的优先级更高, Task1先执行 */
xTaskCreate( vTask1, "Task 1", 1000, NULL, 2, NULL );
xTaskCreate( vTask2, "Task 2", 1000, NULL, 1, NULL );
/* 启动调度器 */
vTaskStartScheduler();
/* 如果程序运行到了这里就表示出错了, 一般是内存不足 */
return 0;
}
void vTask1( void *pvParameters )
{
const TickType_t xDelay50ms = pdMS_TO_TICKS( 50UL );
TickType_t xLastWakeTime;
int i;
/* 获得当前的Tick Count */
xLastWakeTime = xTaskGetTickCount();
for( ;; )
{
flag = 1;
/* 故意加入多个循环,让程序运行时间长一点 */
for (i = 0; i <5; i++)
printf( "Task 1 is running\r\n" );
##if 1
vTaskDelay(xDelay50ms);
##else
vTaskDelayUntil(&xLastWakeTime, xDelay50ms);
##endif
}
}
void vTask2( void *pvParameters )
{
for( ;; )
{
flag = 0;
printf( "Task 2 is running\r\n" );
}
}

使用Keil的逻辑分析观察flag变量的bit波形,如下:
flag为1时表示Task1在运行,flag为0时表示Task2在运行,也就是Task1处于阻塞状态
        vTaskDelay:指定的是阻塞的时间
        vTaskDelayUntil:指定的是任务执行的间隔、周期

六、调度算法 

static volatile int flagIdleTaskrun = 0; // 空闲任务运行时flagIdleTaskrun=1
static volatile int flagTask1run = 0; // 任务1运行时flagTask1run=1
static volatile int flagTask2run = 0; // 任务2运行时flagTask2run=1
static volatile int flagTask3run = 0; // 任务3运行时flagTask3run=1
int main( void )
{
prvSetupHardware();
xTaskCreate(vTask1, "Task 1", 1000, NULL, 0, NULL);
xTaskCreate(vTask2, "Task 2", 1000, NULL, 0, NULL);
xTaskCreate(vTask3, "Task 3", 1000, NULL, 2, NULL);
/* 启动调度器 */
vTaskStartScheduler();
/* 如果程序运行到了这里就表示出错了, 一般是内存不足 */
return 0;
}
void vTask1( void *pvParameters )
{
/* 任务函数的主体一般都是无限循环 */for( ;; ){flagIdleTaskrun = 0;flagTask1run = 1;flagTask2run = 0;flagTask3run = 0;/* 打印任务的信息 */printf("T1\r\n");}
}
void vTask2( void *pvParameters )
{
/* 任务函数的主体一般都是无限循环 */for( ;; ){flagIdleTaskrun = 0;flagTask1run = 0;flagTask2run = 1;flagTask3run = 0;/* 打印任务的信息 */printf("T2\r\n");}
}
void vTask3( void *pvParameters )
{const TickType_t xDelay5ms = pdMS_TO_TICKS( 5UL );/* 任务函数的主体一般都是无限循环 */for( ;; ){flagIdleTaskrun = 0;flagTask1run = 0;flagTask2run = 0;flagTask3run = 1;/* 打印任务的信息 */printf("T3\r\n");// 如果不休眠的话, 其他任务无法得到执行vTaskDelay( xDelay5ms );}    
}

        提供了一个空闲任务的钩子函数:      

void vApplicationIdleHook(void)
{flagIdleTaskrun = 1;flagTask1run = 0;flagTask2run = 0;flagTask3run = 0;/* 故意加入打印让flagIdleTaskrun变为1的时间维持长一点 */printf("Id\r\n");
}

          抢占时:高优先级任务就绪时,就可以马上执行
        不抢占时:优先级失去意义了,既然不能抢占就只能协商了,图中任务1一直在运行(一点都没有协商精神),其他任务都无法执行。即使任务3的vTaskDelay 已经超时、即使它的优先级更高,都没办法执行。

相关文章:

FreeRTOS入门--任务

目录 一、什么是任务 二、创建任务---xTaskCreate函数 三、任务的删除 四、任务优先级 1.阻塞状态(Blocked) 2.暂停状态(Suspended) 3.就绪状态(Ready) 五、Delay 六、调度算法 一、什么是任务 在FreeRTOS中&#xff0c;任务就是一个函数&#xff0c;原型如下&#xff…...

4个解决特定的任务的Pandas高效代码

在本文中&#xff0c;我将分享4个在一行代码中完成的Pandas操作。这些操作可以有效地解决特定的任务&#xff0c;并以一种好的方式给出结果。 从列表中创建字典 我有一份商品清单&#xff0c;我想看看它们的分布情况。更具体地说&#xff1a;希望得到唯一值以及它们在列表中出…...

【已解决】AttributeError: module ‘gradio‘ has no attribute ‘Image‘

问题描述 AttributeError: module gradio has no attribute Image 不知道作者用的是哪个gradio版本&#xff0c;最新的版本报错AttributeError: module gradio has no attribute outputs &#xff0c; 换一个老一点的版本会报错AttributeError: module gradio has no attribute…...

高级软件工程15本书籍

如果您想学习软件工程技能并提高您的专业知识&#xff0c;那么这里是您的最佳选择。我们有一本很棒的书&#xff0c;可以极大地增强您在软件工程方面的知识。 1&#xff09;干净的代码 Robert C. Martin 写了一本名为“干净代码&#xff1a;敏捷软件工艺手册”的书。在本书中&…...

计网Lesson3 - 计算机网络评价指标与封包解包

文章目录 计算机网络的性能指标1. 速率2. 带宽3. 吞吐量4. 时延5. 时延带宽积6. 往返时间7. 利用率8. 数据的解包和封包 计算机网络的术语实体![实体](https://img-blog.csdnimg.cn/direct/cbf4ca9ed5ab4df290b5a17b4642c6a1.png)协议服务 计算机网络的性能指标 1. 速率 数据…...

深度学习好文记录,反复学习

recent update time&#xff1a;2023.12.2 深度学习入门 - 知乎、这本书也很好&#xff0c;作者写的专栏不错。 机器学习&#xff0c;深度学习一些好文_一只菜得不行的鸟的博客-CSDN博客 卷积神经网络学习路线&#xff08;五&#xff09;| 卷积神经网络参数设置&#xff0c;提…...

CSS浅谈动画性能

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 目的一、举个栗子二、性能分析1.从图层分析2.性能分析 总结 目的 为了探究使用动画时&#xff0c;『transform』和『width、height、margin等』的差异 一、举个栗子…...

万能的视频格式播放器

今天博主给大家带来一款“万能”的视频播放器——VLC Media Player&#xff0c;支持的文件格式非常多&#xff0c;大家快来一起看看吧&#xff01; VLC Media Player 是一款可播放大多数格式&#xff0c;而无需安装编解码器包的媒体播放器。可以播放 MPEG-1、MPEG-2、MPEG-4、D…...

设计模式---第五篇

系列文章目录 文章目录 系列文章目录前言一、知道观察者模式吗?前言 前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家。点击跳转到网站,这篇文章男女通用,看懂了就去分享给你的码吧。 一、知道观察者模式吗? 答:观察者模式是定义对…...

.NET8构建统计Extreme Optimization Numerical Libraries

为 .NET 8 构建统计应用程序 Extreme Optimization Numerical Libraries for .NET V8.1.22 添加了对 .NET 8 的支持&#xff0c;使您可以使用最新版本的 Microsoft 平台。 Extreme Optimization Numerical Libraries for .NET 是通用数学和统计类的集合&#xff0c;为技术和统计…...

07-原型模式-C语言实现

原型模式&#xff1a; Specify the kinds of objects to create using a prototypical instance,and create new objects by copying this prototype.&#xff08;用原型实例指定创建对象的种类&#xff0c; 并且通过拷贝这些原型创建新的对象。 &#xff09; UML图&#xff1…...

深度学习与深度迁移学习有什么区别?

深度学习包含深度迁移学习&#xff0c;它们都利用了深层神经网络&#xff08;Deep Neural Network&#xff0c;DNN&#xff09;来处理数据&#xff0c;并从中学习特征。但是&#xff0c;它们也有一些区别。 深度学习是一种机器学习方法&#xff0c;它通过多层神经网络来自动学…...

创建Asp.net MVC项目Ajax实现视图页面数据与后端Json传值显示

简述回顾 继上篇文章创建的mvc传值这里说明一下Json传值。在mvc框架中&#xff0c;不可避免地会遇到前台传值到后台&#xff0c;前台接收后台的值的情况&#xff08;前台指view&#xff0c;后台指controller&#xff09;&#xff0c;有时只需要从控制器中返回一个处理的结果&a…...

1089 Insert or Merge (插入排序,相邻归并排序,附模拟实现)

注意点1&#xff1a;判断插入排序不能从头开始判断是否为目标数组&#xff0c; 比如&#xff1a;初始为1 2 3 4 3&#xff0c;目标数组也为1 2 3 4 3&#xff0c;则如果是从头开始推的&#xff0c;则下一步会变成1 2 3 4 3&#xff0c;而下一步应该是 1 2 3 3 4。所以我们应该…...

基于算能的国产AI边缘计算盒子8核心A53丨17.6Tops算力

边缘计算盒子 8核心A53丨17.6Tops算力 ● 可提供17.6TOPS&#xff08;INT8&#xff09;的峰值计算能力、2.2TFLOPS&#xff08;FP32&#xff09;的高精度算力&#xff0c;单芯片最高支持32路H.264 & H.265的实时解码能力。 ● 适配Caffe/TensorFlow/MxNet/PyTorch/ ONNX/…...

Eaxyx 让圆球跟随鼠标移动

如果出现2023&#xff0c;代表配置成功: 进入Eaxy官方网站&#xff0c;点击文档&#xff1a; 选择 函数->绘图函数->initgraph: 可以看见initgraph&#xff08;&#xff09;函数有如下三个参数: 现在我们想生成一个1280*720大小的窗口&#xff1a; 我们需写如下代码: 但…...

Node.js 事件循环:定时任务、延迟任务和 I/O 事件的艺术

&#x1f90d; 前端开发工程师&#xff08;主业&#xff09;、技术博主&#xff08;副业&#xff09;、已过CET6 &#x1f368; 阿珊和她的猫_CSDN个人主页 &#x1f560; 牛客高级专题作者、在牛客打造高质量专栏《前端面试必备》 &#x1f35a; 蓝桥云课签约作者、已在蓝桥云…...

陪诊系统:基于自然语言处理的患者沟通创新

医疗领域的数字化转型正日益引入创新技术&#xff0c;其中基于自然语言处理&#xff08;NLP&#xff09;的陪诊系统成为提升患者沟通的一项关键技术。本文将深入研究这一领域&#xff0c;介绍陪诊系统如何借助NLP实现患者沟通的创新&#xff0c;并提供一个简单的Python代码示例…...

实用攻略——SD-WAN网络配置步骤详解

SD-WAN&#xff08;软件定义广域网&#xff09;作为一种新兴的网络技术&#xff0c;被广泛应用于构建高效、可靠的企业组网。 本文将详细介绍企业组网中SD-WAN涉及的配置过程&#xff0c;并提供一些配置技巧&#xff0c;以帮助企业快速了解企业组网的配置。通过使用SD-WAN技术&…...

​无人机摄影测量

无人机摄影测量技术是传统航空摄影测量手段的有力补充&#xff0c;具有机动灵活、高效快速、精细准确、作业成本低、生产周期短、影像获取空间分辨率高、高危地区探测等优势。无人机与航空摄影测量相结合使得“无人机数字低空遥感”成为航空遥感领域的一个崭新发展方向。无人机…...

springboot 百货中心供应链管理系统小程序

一、前言 随着我国经济迅速发展&#xff0c;人们对手机的需求越来越大&#xff0c;各种手机软件也都在被广泛应用&#xff0c;但是对于手机进行数据信息管理&#xff0c;对于手机的各种软件也是备受用户的喜爱&#xff0c;百货中心供应链管理系统被用户普遍使用&#xff0c;为方…...

day52 ResNet18 CBAM

在深度学习的旅程中&#xff0c;我们不断探索如何提升模型的性能。今天&#xff0c;我将分享我在 ResNet18 模型中插入 CBAM&#xff08;Convolutional Block Attention Module&#xff09;模块&#xff0c;并采用分阶段微调策略的实践过程。通过这个过程&#xff0c;我不仅提升…...

电脑插入多块移动硬盘后经常出现卡顿和蓝屏

当电脑在插入多块移动硬盘后频繁出现卡顿和蓝屏问题时&#xff0c;可能涉及硬件资源冲突、驱动兼容性、供电不足或系统设置等多方面原因。以下是逐步排查和解决方案&#xff1a; 1. 检查电源供电问题 问题原因&#xff1a;多块移动硬盘同时运行可能导致USB接口供电不足&#x…...

全球首个30米分辨率湿地数据集(2000—2022)

数据简介 今天我们分享的数据是全球30米分辨率湿地数据集&#xff0c;包含8种湿地亚类&#xff0c;该数据以0.5X0.5的瓦片存储&#xff0c;我们整理了所有属于中国的瓦片名称与其对应省份&#xff0c;方便大家研究使用。 该数据集作为全球首个30米分辨率、覆盖2000–2022年时间…...

python如何将word的doc另存为docx

将 DOCX 文件另存为 DOCX 格式&#xff08;Python 实现&#xff09; 在 Python 中&#xff0c;你可以使用 python-docx 库来操作 Word 文档。不过需要注意的是&#xff0c;.doc 是旧的 Word 格式&#xff0c;而 .docx 是新的基于 XML 的格式。python-docx 只能处理 .docx 格式…...

Springcloud:Eureka 高可用集群搭建实战(服务注册与发现的底层原理与避坑指南)

引言&#xff1a;为什么 Eureka 依然是存量系统的核心&#xff1f; 尽管 Nacos 等新注册中心崛起&#xff0c;但金融、电力等保守行业仍有大量系统运行在 Eureka 上。理解其高可用设计与自我保护机制&#xff0c;是保障分布式系统稳定的必修课。本文将手把手带你搭建生产级 Eur…...

Redis数据倾斜问题解决

Redis 数据倾斜问题解析与解决方案 什么是 Redis 数据倾斜 Redis 数据倾斜指的是在 Redis 集群中&#xff0c;部分节点存储的数据量或访问量远高于其他节点&#xff0c;导致这些节点负载过高&#xff0c;影响整体性能。 数据倾斜的主要表现 部分节点内存使用率远高于其他节…...

Linux离线(zip方式)安装docker

目录 基础信息操作系统信息docker信息 安装实例安装步骤示例 遇到的问题问题1&#xff1a;修改默认工作路径启动失败问题2 找不到对应组 基础信息 操作系统信息 OS版本&#xff1a;CentOS 7 64位 内核版本&#xff1a;3.10.0 相关命令&#xff1a; uname -rcat /etc/os-rele…...

C++:多态机制详解

目录 一. 多态的概念 1.静态多态&#xff08;编译时多态&#xff09; 二.动态多态的定义及实现 1.多态的构成条件 2.虚函数 3.虚函数的重写/覆盖 4.虚函数重写的一些其他问题 1&#xff09;.协变 2&#xff09;.析构函数的重写 5.override 和 final关键字 1&#…...

【JVM面试篇】高频八股汇总——类加载和类加载器

目录 1. 讲一下类加载过程&#xff1f; 2. Java创建对象的过程&#xff1f; 3. 对象的生命周期&#xff1f; 4. 类加载器有哪些&#xff1f; 5. 双亲委派模型的作用&#xff08;好处&#xff09;&#xff1f; 6. 讲一下类的加载和双亲委派原则&#xff1f; 7. 双亲委派模…...