FreeRTOS从入门到精通 第十六章(任务通知)

参考教程:【正点原子】手把手教你学FreeRTOS实时系统_哔哩哔哩_bilibili

一、任务通知简介

1、概述

(1)任务通知顾名思义是用来通知任务的,任务控制块中的结构体成员变量ulNotifiedValue就是这个通知值。

(2)队列、信号量、事件标志组与任务通知的区别:

①使用队列、信号量、事件标志组时都需另外创建一个结构体,通过中间的结构体进行任务间的通信。

②使用任务通知时,任务结构体TCB中就包含了内部对象,可以直接接收其它任务发过来的“通知”。

(3)只要合理、灵活地利用任务通知的特点,可以在一些场合中替代队列、信号量、事件标志组。

(4)任务通知的优势及劣势:

①优势:

[1]使用任务通知向任务发送事件或数据,比使用队列、事件标志组或信号量快得多。

[2]使用其它方法进行任务间通讯时都要先创建对应的结构体,而使用任务通知则无需额外创建结构体。

②劣势:

[1]ISR没有任务结构体,所以无法给ISR发送数据,但是ISR可以使用任务通知的功能,发数据给任务。

[2]任务通知只能是被指定的一个任务接收并处理,无法广播给多个任务。

[3]任务通知是通过更新任务通知值来发送数据的,任务结构体中只有一个任务通知值,只能保持一个数据,无法缓存多个数据。

[4]发送方无法进入阻塞状态等待。

2、任务通知值和通知状态

(1)任务都有一个结构体——任务控制块TCB,它里边有两个结构体成员变量与任务通知相关,一个是uint32_t类型,用来表示通知值,另一个是uint8_t类型,用来表示通知状态。

typedef struct tskTaskControlBlock 
{
	… …
    #if ( configUSE_TASK_NOTIFICATIONS  ==  1 )
      volatile uint32_t ulNotifiedValue[configTASK_NOTIFICATION_ARRAY_ENTRIES];
      volatile uint8_t ucNotifyState[configTASK_NOTIFICATION_ARRAY_ENTRIES];
    #endif
	… …
}tskTCB;
#define configTASK_NOTIFICATION_ARRAY_ENTRIES	1  	//定义任务通知数组的大小,默认为1

(2)任务通知值的更新方式有多种类型:

①计数值(数值累加,类似信号量)。

②相应位置1(类似事件标志组)。

③写任意数值(支持覆写和不覆写,类似队列)。

(3)任务通知状态共有3种取值:

①任务未等待通知:任务通知默认的初始化状态。

②任务在等待通知:接收方已经准备好了(调用了接收任务通知函数),等待发送方给通知。

③任务在等待接收:发送方已经将通知发送出去(调用了发送任务通知函数),等待接收方接收。

#define	    taskNOT_WAITING_NOTIFICATION ( ( uint8_t ) 0 )		//任务未等待通知
#define 	taskWAITING_NOTIFICATION	 ( ( uint8_t ) 1 )		//任务在等待通知
#define 	taskNOTIFICATION_RECEIVED    ( ( uint8_t ) 2 )		//任务在等待接收

二、任务通知相关API函数介绍

1、任务通知相关API函数概览

(1)任务通知API函数主要有两类——发送通知、接收通知,发送通知API函数可以用于任务和中断服务函数中,而接收通知API函数只能用在任务中。

(2)发送通知相关API函数:

函数

描述

xTaskNotify()

发送通知,带有通知值

xTaskNotifyAndQuery()

发送通知,带有通知值并且保留接收任务的原通知值

xTaskNotifyGive()

发送通知,不带通知值

xTaskNotifyFromISR()

在中断中发送任务通知

xTaskNotifyAndQueryFromISR()

vTaskNotifyGiveFromISR()

(3)接收通知相关API函数:

函数

描述

ulTaskNotifyTake()

获取任务通知,可以设置在退出此函数的时候将任务通知值清零或者减一;

当任务通知用作二值信号量或者计数信号量的时候,使用此函数来获取信号量

xTaskNotifyWait()

获取任务通知,比 ulTaskNotifyTak()更为复杂,可获取通知值和清除通知值的指定位;

当任务通知用作于事件标志组或队列时,使用此函数来获取

2、发送通知相关API函数

(1)xTaskNotifyAndQuery函数、xTaskNotify函数和xTaskNotifyGive函数,它们的底层函数实际上都是同一个函数。

#define xTaskNotifyAndQuery(xTaskToNotify, ulValue, eAction, pulPreviousNotifyValue)	 \   
		xTaskGenericNotify( ( xTaskToNotify ), 
					  ( tskDEFAULT_INDEX_TO_NOTIFY ), 
					  ( ulValue ), 
					  ( eAction ),
					  ( pulPreviousNotifyValue ) )

#define	xTaskNotify(xTaskToNotify, ulValue, eAction) 	 \   
 		xTaskGenericNotify( ( xTaskToNotify ),
 					  ( tskDEFAULT_INDEX_TO_NOTIFY ), 
					  ( ulValue ), 
					  ( eAction ), 
					  NULL )

#define 	xTaskNotifyGive(xTaskToNotify)	  \   
		xTaskGenericNotify( ( xTaskToNotify ),
 					  ( tskDEFAULT_INDEX_TO_NOTIFY ),
 					  ( 0 ),
 					  eIncrement,
 					  NULL )

(2)xTaskGenericNotify函数:

①函数入口定义:

BaseType_t xTaskGenericNotify
(
    TaskHandle_t  	xTaskToNotify,        //接收任务通知的任务句柄
    UBaseType_t 		uxIndexToNotify,      //任务的指定通知(任务通知相关数组成员)
    uint32_t 			ulValue,             //任务通知值
    eNotifyAction 	eAction,             //通知方式(通知值更新方式)
    uint32_t * 		pulPreviousNotificationValue  //用于保存更新前的任务通知值(为NULL则不保存)
)

②任务通知方式共有以下几种:

typedef  enum
{    
	eNoAction = 0, 			//无操作
	eSetBits					//更新指定bit
	eIncrement				//通知值加1
 	eSetValueWithOverwrite	//覆写的方式更新通知值
	eSetValueWithoutOverwrite	//不覆写通知值
}eNotifyAction;

③函数源码剖析:

BaseType_t xTaskGenericNotify( TaskHandle_t xTaskToNotify,
                               UBaseType_t uxIndexToNotify,
                               uint32_t ulValue,
                               eNotifyAction eAction,
                               uint32_t * pulPreviousNotificationValue )
{
    TCB_t * pxTCB;
    BaseType_t xReturn = pdPASS;
    uint8_t ucOriginalNotifyState;

    traceENTER_xTaskGenericNotify( xTaskToNotify, uxIndexToNotify, ulValue, eAction, pulPreviousNotificationValue );

    configASSERT( uxIndexToNotify < configTASK_NOTIFICATION_ARRAY_ENTRIES );
    configASSERT( xTaskToNotify );
    pxTCB = xTaskToNotify;

    taskENTER_CRITICAL();
    {
        if( pulPreviousNotificationValue != NULL )  //判断是否需要保存原先的任务通知值
        {
            *pulPreviousNotificationValue = pxTCB->ulNotifiedValue[ uxIndexToNotify ];  //保存原先的任务通知值至缓冲区
        }

        //记录目标任务先前的通知状态
        ucOriginalNotifyState = pxTCB->ucNotifyState[ uxIndexToNotify ];

		//赋值当前的任务状态(等待接收状态,即发送方的数据等待接收方接收)
        pxTCB->ucNotifyState[ uxIndexToNotify ] = taskNOTIFICATION_RECEIVED;

        switch( eAction )    //有四种通知值的更新方式,需要判断使用哪种
        {
            case eSetBits:                  //按位更新(类似事件标志组)
                pxTCB->ulNotifiedValue[ uxIndexToNotify ] |= ulValue;
                break;

            case eIncrement:                //用计数的方式更新通知值(类似信号量)
                ( pxTCB->ulNotifiedValue[ uxIndexToNotify ] )++;
                break;

            case eSetValueWithOverwrite:     //覆写的方式更新通知值(类似队列)
                pxTCB->ulNotifiedValue[ uxIndexToNotify ] = ulValue;
                break;

            case eSetValueWithoutOverwrite:  //不覆写的方式更新通知值(类似队列)

                if( ucOriginalNotifyState != taskNOTIFICATION_RECEIVED )
                {
                    pxTCB->ulNotifiedValue[ uxIndexToNotify ] = ulValue;
                }
                else
                {
                    xReturn = pdFAIL;   //如果有通知值还未被接收,不能覆写
                }
                break;

            case eNoAction:
                break;

            default:
                configASSERT( xTickCount == ( TickType_t ) 0 );

                break;
        }

        traceTASK_NOTIFY( uxIndexToNotify );

        if( ucOriginalNotifyState == taskWAITING_NOTIFICATION )   //如果接收方已准备好接收通知值
        {
            listREMOVE_ITEM( &( pxTCB->xStateListItem ) );   //将接收方任务从阻塞列表中移出
            prvAddTaskToReadyList( pxTCB );       //将接收方任务添加进就绪列表中

            configASSERT( listLIST_ITEM_CONTAINER( &( pxTCB->xEventListItem ) ) == NULL );

            #if ( configUSE_TICKLESS_IDLE != 0 )
            {
                prvResetNextTaskUnblockTime();
            }
            #endif

            taskYIELD_ANY_CORE_IF_USING_PREEMPTION( pxTCB );  //如因优先级问题需要任务切换,执行即可
        }
        else
        {
            mtCOVERAGE_TEST_MARKER();
        }
    }
    taskEXIT_CRITICAL();

    traceRETURN_xTaskGenericNotify( xReturn );

    return xReturn;
}

3、接收通知相关API函数

(1)ulTaskNotifyTake函数:

①函数定义:

#define ulTaskNotifyTake(xClearCountOnExit, xTicksToWait) 				\

ulTaskGenericNotifyTake( ( tskDEFAULT_INDEX_TO_NOTIFY ),			\
					 ( xClearCountOnExit ), 						\
					 ( xTicksToWait ) ) 

②函数参数:

形参

描述

xClearCountOnExit 

指定在成功接收通知后,将通知值清零或减 1

pdTRUE:把通知值清零;pdFALSE:把通知值减1

xTicksToWait 

阻塞等待任务通知值的最大时间

③返回值:

返回值

描述

0

接收失败

非 0 

接收成功,返回任务通知的通知值

④ulTaskGenericNotifyTake函数源码剖析:

uint32_t ulTaskGenericNotifyTake( UBaseType_t uxIndexToWaitOn,
                                  BaseType_t xClearCountOnExit,
                                  TickType_t xTicksToWait )
{
    uint32_t ulReturn;
    BaseType_t xAlreadyYielded, xShouldBlock = pdFALSE;

   traceENTER_ulTaskGenericNotifyTake(uxIndexToWaitOn,xClearCountOnExit,xTicksToWait);
    configASSERT( uxIndexToWaitOn < configTASK_NOTIFICATION_ARRAY_ENTRIES );

    vTaskSuspendAll();
    {
        taskENTER_CRITICAL();
        {
            if( pxCurrentTCB->ulNotifiedValue[ uxIndexToWaitOn ] == 0U )  //判断任务通知值是否为0,是则说明发送方还未发送非0通知值
            {
                pxCurrentTCB->ucNotifyState[ uxIndexToWaitOn ] = taskWAITING_NOTIFICATION;  //将状态更改为在等待通知

                if( xTicksToWait > ( TickType_t ) 0 )  //如果阻塞时间大于0
                    xShouldBlock = pdTRUE;  //任务需要阻塞
                else
                    mtCOVERAGE_TEST_MARKER();
            }
            else
            {
                mtCOVERAGE_TEST_MARKER();
            }
        }
        taskEXIT_CRITICAL();

        if( xShouldBlock == pdTRUE )  //如果任务需要阻塞
        {
            traceTASK_NOTIFY_TAKE_BLOCK( uxIndexToWaitOn );
            prvAddCurrentTaskToDelayedList( xTicksToWait, pdTRUE ); //将任务添加进阻塞列表中
        }
        else
        {
            mtCOVERAGE_TEST_MARKER();
        }
    }
    xAlreadyYielded = xTaskResumeAll();

    if( ( xShouldBlock == pdTRUE ) && ( xAlreadyYielded == pdFALSE ) )
    {
        taskYIELD_WITHIN_API();  //如果任务进入阻塞态,则需要执行任务切换(if判断的作用是防止重复切换)
    }
    else
    {
        mtCOVERAGE_TEST_MARKER();
    }

    taskENTER_CRITICAL();
    {
        traceTASK_NOTIFY_TAKE( uxIndexToWaitOn );
        ulReturn = pxCurrentTCB->ulNotifiedValue[ uxIndexToWaitOn ];

        if( ulReturn != 0U )   //如果任务通知值不为0,说明发送方已将非0通知值发送
        {
            if( xClearCountOnExit != pdFALSE )  //根据函数参数判断通知值如何处理
            {
                pxCurrentTCB->ulNotifiedValue[ uxIndexToWaitOn ] = ( uint32_t ) 0U; 
                //通知值清零(用于模拟二值信号量)
            }
            else
            {
                pxCurrentTCB->ulNotifiedValue[uxIndexToWaitOn] = ulReturn - (uint32_t)1;  
                //通知值-1(用于模拟计数型信号量)
            }
        }
        else
        {
            mtCOVERAGE_TEST_MARKER();
        }

        pxCurrentTCB->ucNotifyState[ uxIndexToWaitOn ] = taskNOT_WAITING_NOTIFICATION;  //状态更改为未等待通知
    }
    taskEXIT_CRITICAL();

    traceRETURN_ulTaskGenericNotifyTake( ulReturn );

    return ulReturn;
}

(2)xTaskNotifyWait函数:

①函数定义:

#define xTaskNotifyWait(ulBitsToClearOnEntry, 				\
				ulBitsToClearOnExit, 						\
				pulNotificationValue, 						\
				xTicksToWait) 							\

xTaskGenericNotifyWait(tskDEFAULT_INDEX_TO_NOTIFY, 	\
				( ulBitsToClearOnEntry ), 					\
				( ulBitsToClearOnExit ), 					\
				( pulNotificationValue ), 					\
				( xTicksToWait )) 

②函数参数:

形参

描述

uxIndexToWaitOn 

任务的指定通知(任务通知相关数组成员)

ulBitesToClearOnEntry 

等待前清零指定任务通知值的比特位(旧值对应bit清0)

ulBitesToClearOnExit 

成功等待后清零指定的任务通知值比特位(新值对应bit清0)

pulNotificationValue 

用来取出通知值(如果不需要取出,可设为NULL)

xTicksToWait 

阻塞等待任务通知值的最大时间

③返回值:

返回值

描述

pdTRUE 

等待任务通知成功

pdFALSE 

等待任务通知失败

④xTaskGenericNotifyWait函数源码剖析:

BaseType_t xTaskGenericNotifyWait( UBaseType_t uxIndexToWaitOn,
                                   uint32_t ulBitsToClearOnEntry,
                                   uint32_t ulBitsToClearOnExit,
                                   uint32_t * pulNotificationValue,
                                   TickType_t xTicksToWait )
{
    BaseType_t xReturn, xAlreadyYielded, xShouldBlock = pdFALSE;

    traceENTER_xTaskGenericNotifyWait( uxIndexToWaitOn, ulBitsToClearOnEntry, ulBitsToClearOnExit, pulNotificationValue, xTicksToWait );

    configASSERT( uxIndexToWaitOn < configTASK_NOTIFICATION_ARRAY_ENTRIES );

    vTaskSuspendAll();
    {
        taskENTER_CRITICAL();
        {
            if( pxCurrentTCB->ucNotifyState[ uxIndexToWaitOn ] != taskNOTIFICATION_RECEIVED )  //判断任务是否不在等待接收
            {
                pxCurrentTCB->ulNotifiedValue[ uxIndexToWaitOn ] &= ~ulBitsToClearOnEntry;       //将原先通知值的指定位清零

                pxCurrentTCB->ucNotifyState[ uxIndexToWaitOn ] = taskWAITING_NOTIFICATION;     //将任务状态更改为在等待通知

                if( xTicksToWait > ( TickType_t ) 0 )  //如果阻塞时间大于0
                    xShouldBlock = pdTRUE;  //任务需要阻塞
                else
                    mtCOVERAGE_TEST_MARKER();
            }
            else
            {
                mtCOVERAGE_TEST_MARKER();
            }
        }
        taskEXIT_CRITICAL();

        if( xShouldBlock == pdTRUE )  //如果任务需要阻塞
        {
            traceTASK_NOTIFY_WAIT_BLOCK( uxIndexToWaitOn );
            prvAddCurrentTaskToDelayedList( xTicksToWait, pdTRUE );  //将任务添加进阻塞列表中
        }
        else
        {
            mtCOVERAGE_TEST_MARKER();
        }
    }
    xAlreadyYielded = xTaskResumeAll();

    if( ( xShouldBlock == pdTRUE ) && ( xAlreadyYielded == pdFALSE ) )
    {
        taskYIELD_WITHIN_API();  //如果任务进入阻塞态,则需要执行任务切换(if判断的作用是防止重复切换)
    }
    else
    {
        mtCOVERAGE_TEST_MARKER();
    }

    taskENTER_CRITICAL();
    {
        traceTASK_NOTIFY_WAIT( uxIndexToWaitOn );

        if( pulNotificationValue != NULL )  //判断是否需要保存原先的任务通知值
        {
            *pulNotificationValue = pxCurrentTCB->ulNotifiedValue[ uxIndexToWaitOn ];  //保存原先的任务通知值
        }

        if( pxCurrentTCB->ucNotifyState[ uxIndexToWaitOn ] != taskNOTIFICATION_RECEIVED )  //判断任务是否不在等待接收
        {
            xReturn = pdFALSE;  //接收失败
        }
        else
        {
            pxCurrentTCB->ulNotifiedValue[ uxIndexToWaitOn ] &= ~ulBitsToClearOnExit;   //将通知值接收,并把指定位清零
            xReturn = pdTRUE;   //接收成功
        }

        pxCurrentTCB->ucNotifyState[ uxIndexToWaitOn ] = taskNOT_WAITING_NOTIFICATION;  //状态更改为未等待通知
    }
    taskEXIT_CRITICAL();

    traceRETURN_xTaskGenericNotifyWait( xReturn );

    return xReturn;
}

三、任务通知模拟二值信号量实验

1、原理图与实验目标

(1)原理图:

(2)实验目标:

①设计4个任务——start_task、task1、task2、task3:

[1]start_task:用于创建task1、task2和task3任务。

[2]task1:当获取到LED1的硬件资源后,控制LED1约每500ms完成亮暗翻转的状态切换,每完成一次即将资源释放。

[3]task2:当获取到LED2的硬件资源后,控制LED2约每1000ms完成亮暗翻转的状态切换,每完成一次即将资源释放。

[4]task3:按下按键1,获取(或者说霸占)LED1和LED2的硬件资源;按下按键2,释放LED1和LED2的硬件资源。

②预期实验现象:

[1]程序下载到板子上后,两个LED灯闪烁。

[2]按下按键1,LED1和LED2停止闪烁(允许有1秒左右的延迟)。

[3]按下按键2,LED1和LED2恢复闪烁。

2、实验步骤

(1)将“二值信号量实验”的工程文件夹复制一份,在拷贝版中进行实验。

(2)将FreeRTOS_experiment.c文件中关于信号量的代码全部移除,并更改task1、task2和task3函数的实现。

①task1函数的思路:需要使用LED的硬件资源时调用ulTaskNotifyTake函数等待任务通知,并且必须等待到通知(同时将通知值清零)才可进行下一步操作——翻转LED1的状态。

②task2函数的思路:需要使用LED的硬件资源时调用ulTaskNotifyTake函数等待任务通知,并且必须等待到通知(同时将通知值清零)才可进行下一步操作——翻转LED2的状态。

③task3函数的思路:

[1]task1和task2需要task3的通知才能使用LED硬件资源,与二值信号量实验不同,二值信号量实验有单独的两个“队列”分别管理两个LED硬件资源,而本实验则是把task3当作了二值信号量队列管理员,两个二值信号量分别由task1和task2的TCB的任务通知相关成员代替。

[2]task1和task2不断申请和归还硬件资源,那么task3也要不断处理task1和task2的申请,要不断地管理资源的分配,而不是等待按键事件到来的一刻才做一次资源分配操作,于是原本的控制算法需要做变更。

[3]在未按下任何按键时,task3可以一直做释放LED硬件资源给task1和task2的操作,即使手上没有LED资源也不会陷入阻塞,而是直接执行下一条语句(当然,在实际项目中通常不建议这么做,此处仅仅是为了功能演示),task1和task2哪个先结束阻塞,哪个就先调用ulTaskNotifyTake函数等待任务通知,或者已经调用ulTaskNotifyTake函数但之前未等待到通知而进入无限阻塞,但task3一旦分配资源以后就能立刻被唤醒。

[4]在按下按键1之后,task3不做资源分配操作,task1和task2只能无限等待任务通知,无法执行后续的任何操作。

[5]在按下按键2之后(以下执行流程图的情形为按下按键1后再按下按键2),task3可以继续一直做释放LED硬件资源给task1和task2的操作。

void task1(void)
{
	while(1)
	{
		ulTaskNotifyTake(pdTRUE, portMAX_DELAY); //接收LED硬件资源的使用通知
		LED1_Turn();      //LED1状态翻转
		vTaskDelay(500);   //延时(自我阻塞)500ms
	}
}

void task2(void)
{
	while(1)
	{
		ulTaskNotifyTake(pdTRUE, portMAX_DELAY); //接收LED硬件资源的使用通知
		LED2_Turn();      //LED2状态翻转
		vTaskDelay(1000);  //延时(自我阻塞)1000ms
	}
}

void task3(void)
{
	uint8_t Key_memory, key = 0;
	while(1)
	{
		key = Key_GetNum();    //读取按键键值
		if(key != 0)
			Key_memory = key;
		if(Key_memory == 1);
		if(Key_memory == 2 || Key_memory == 0)
		{
			xTaskNotifyGive(task1_handler);         //释放LED硬件资源给task1
			xTaskNotifyGive(task2_handler);         //释放LED硬件资源给task2
		}
		vTaskDelay(10);  //延时(自我阻塞)10ms
	}
}

(3)程序完善好后点击“编译”,然后将程序下载到开发板上,根据程序注释进行调试。

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

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

相关文章

数据结构选讲 (更新中)

参考 smWCDay7 数据结构选讲2 by yyc 。 可能会补充的&#xff1a; AT_cf17_final_j TreeMST 的 F2 Boruvka算法 目录 AT_cf17_final_j Tree MSTP5280 [ZJOI2019] 线段树 AT_cf17_final_j Tree MST link 题意 给定一棵 n n n 个点的树&#xff0c;点有点权 w i w_i wi​&am…

【01】共识机制

BTF共识 拜占庭将军问题 拜占庭将军问题是一个共识问题 起源 Leslie Lamport在论文《The Byzantine Generals Problem》提出拜占庭将军问题。 核心描述 军中可能有叛徒&#xff0c;却要保证进攻一致&#xff0c;由此引申到计算领域&#xff0c;发展成了一种容错理论。随着…

春晚舞台上的人形机器人:科技与文化的奇妙融合

文章目录 人形机器人Unitree H1的“硬核”实力传统文化与现代科技的创新融合网友热议与文化共鸣未来展望&#xff1a;科技与文化的更多可能结语 2025 年央视春晚的舞台&#xff0c;无疑是全球华人目光聚焦的焦点。就在这个盛大的舞台上&#xff0c;一场名为《秧BOT》的创意融合…

.NET Core缓存

目录 缓存的概念 客户端响应缓存 cache-control 服务器端响应缓存 内存缓存&#xff08;In-memory cache&#xff09; 用法 GetOrCreateAsync 缓存过期时间策略 缓存的过期时间 解决方法&#xff1a; 两种过期时间策略&#xff1a; 绝对过期时间 滑动过期时间 两…

如何从客观角度批判性阅读分析博客

此文仅以个人博客为例&#xff0c;大量阅读朋友反馈给我的交流让我得知他们所理解我的博客所表达的意思并非我所想表达的&#xff0c;差异或大或小&#xff0c;因人而异。 观点与事实 只有从客观角度反复批判性阅读和分析&#xff0c;才能逐渐清晰观点和事实。 观点不等于事实…

【力扣】49.字母异位词分组

AC截图 题目 思路 由于互为字母异位词的两个字符串包含的字母相同&#xff0c;因此对两个字符串分别进行排序之后得到的字符串一定是相同的&#xff0c;故可以将排序之后的字符串作为哈希表的键。 可以遍历strs&#xff0c;将其中每一个str排序&#xff0c;然后用unodered_ma…

【4Day创客实践入门教程】Day4 迈向高手之路——进一步学习!

Day4 迈向高手之路——进一步学习&#xff01; 目录 Day4 迈向高手之路——进一步学习&#xff01;更多的开发板外壳制作 Day0 创想启程——课程与项目预览Day1 工具箱构建——开发环境的构建Day2 探秘微控制器——单片机与MicroPython初步Day3 实战演练——桌面迷你番茄钟Day4…

什么是线性化PDF?

线性化PDF是一种特殊的PDF文件组织方式。 总体而言&#xff0c;PDF是一种极为优雅且设计精良的格式。PDF由大量PDF对象构成&#xff0c;这些对象用于创建页面。相关信息存储在一棵二叉树中&#xff0c;该二叉树同时记录文件中每个对象的位置。因此&#xff0c;打开文件时只需加…

向下调整算法(详解)c++

算法流程&#xff1a; 与⽗结点的权值作⽐较&#xff0c;如果⽐它⼤&#xff0c;就与⽗亲交换&#xff1b; 交换完之后&#xff0c;重复 1 操作&#xff0c;直到⽐⽗亲⼩&#xff0c;或者换到根节点的位置 大家可能会有点疑惑&#xff0c;这个是大根堆&#xff0c;22是怎么跑到…

unity学习25:用 transform 进行旋转和移动,简单的太阳地球月亮模型,以及父子级关系

目录 备注内容 1游戏物体的父子级关系 1.1 父子物体 1.2 坐标关系 1.3 父子物体实际是用 每个gameobject的tranform来关联的 2 获取gameObject的静态数据 2.1 具体命令 2.2 具体代码 2.3 输出结果 3 获取gameObject 的方向 3.1 游戏里默认的3个方向 3.2 获取方向代…

C基础算法与实现

前言 通过业务侧输入需求,使用代码完成。 1.偶数立方和 编写函数求1~100中奇数的平方与偶数的立方的和 1.1代码实现结果 1.2源码示例 #include <stdio.h>// 计算1到100中奇数的平方与偶数的立方的和 int calculateSum() {int sum 0;// 遍历1到100之间的所有数字for (…

基于SSM实现的乡村振兴文化平台系统功能实现十八

一、前言介绍&#xff1a; 1.1 项目摘要 农耕文明是广大群众在几千年的农业生产生活中智慧的结晶&#xff0c;不仅是乡土文化的核心和精髓&#xff0c;还是中华文明的起源和基因。因此&#xff0c;传承和发扬优秀乡村文化&#xff0c;是传承农耕文明的必然要求。 文化振兴是乡…

如何让一个用户具备创建审批流程的权限

最近碰到一个问题&#xff0c;两个sandbox&#xff0c;照理用户的权限应该是一样的&#xff0c;结果开发环境里面我可以左右的做各种管理工作&#xff0c;但是使用change set上传后&#xff0c;另一个环境的同一个用户&#xff0c;没有相对于的权限&#xff0c;权限不足。 当时…

实现B-树

一、概述 1.历史 B树&#xff08;B-Tree&#xff09;结构是一种高效存储和查询数据的方法&#xff0c;它的历史可以追溯到1970年代早期。B树的发明人Rudolf Bayer和Edward M. McCreight分别发表了一篇论文介绍了B树。这篇论文是1972年发表于《ACM Transactions on Database S…

解锁维特比算法:探寻复杂系统的最优解密码

引言 在复杂的技术世界中&#xff0c;维特比算法以其独特的魅力和广泛的应用&#xff0c;成为通信、自然语言处理、生物信息学等领域的关键技术。今天&#xff0c;让我们一同深入探索维特比算法的奥秘。 一、维特比算法的诞生背景 维特比算法由安德鲁・维特比在 1967 年提出…

CPU 100% 出现系统中断 怎么解决

CPU 100% 出现系统中断 怎么解决 电脑开机时会掉帧&#xff0c;切换到桌面时就会卡顿&#xff0c;然后打开任务管理器就会看到系统中断的cpu占用率达到100%&#xff0c;过一段时间再打开还是会有显示100%的占用率&#xff0c;这个问题怎么解决&#xff1f; 文章目录 CPU 100% …

Python 梯度下降法(五):Adam Optimize

文章目录 Python 梯度下降法&#xff08;五&#xff09;&#xff1a;Adam Optimize一、数学原理1.1 介绍1.2 符号说明1.3 实现流程 二、代码实现2.1 函数代码2.2 总代码2.3 遇到的问题2.4 算法优化 三、优缺点3.1 优点3.2 缺点 Python 梯度下降法&#xff08;五&#xff09;&am…

labelme_json_to_dataset ValueError: path is on mount ‘D:‘,start on C

这是你的labelme运行时label照片的盘和保存目的地址的盘不同都值得报错 labelme_json_to_dataset ValueError: path is on mount D:,start on C 只需要放一个盘但可以不放一个目录

中间件安全

一.中间件概述 1.中间件定义 介绍&#xff1a;中间件&#xff08;Middleware&#xff09;作为一种软件组件&#xff0c;在不同系统、应用程序或服务间扮演着数据与消息传递的关键角色。它常处于应用程序和操作系统之间&#xff0c;就像一座桥梁&#xff0c;负责不同应用程序间…

玩转大语言模型——配置图数据库Neo4j(含apoc插件)并导入GraphRAG生成的知识图谱

系列文章目录 玩转大语言模型——使用langchain和Ollama本地部署大语言模型 玩转大语言模型——ollama导入huggingface下载的模型 玩转大语言模型——langchain调用ollama视觉多模态语言模型 玩转大语言模型——使用GraphRAGOllama构建知识图谱 玩转大语言模型——完美解决Gra…