FreeRTOS-事件组详解

news2025/2/25 0:41:14

✅作者简介:嵌入式入坑者,与大家一起加油,希望文章能够帮助各位!!!!
📃个人主页:@rivencode的个人主页
🔥系列专栏:玩转FreeRTOS
💬保持学习、保持热爱、认真分享、一起进步!!!

目录

  • 前言
  • 一、事件组的简介
  • 二、事件组源码分析
    • 1.事件组的创建
    • 2.等待事件函数 xEventGroupWaitBits()
    • 3.事件组置位函数 xEventGroupSetBits()
    • 4.任务同步函数xEventGroupSync
  • 三、总结

前言

很久没有更新了,主要是因为在实习,忙论文的事,最主要的是事把又模电重新完完整整学了一遍,感觉我对电路的理解得到了升华,接下来继续把FreeRTOS更新完,最后上STM32单片机大师篇,前面已经把FreeRTOS最重要的应用,队列与信号量讲解完了,核心其实已经OK了,接下来就是将Freertos其他应用讲解完,本文要讲解的事件组应用非常简单,所以我们直接源码讲解,理解到底是如何实现的。

一、事件组的简介

事件是一种实现任务/中断间通信的机制,主要用于实现多任务间的同步,但事件通信只能
是事件类型的通信,无数据传输。其实事件组的本质就是一个整数(16/32位)。

  • 与队列/信号量的区别:

1.信号量/队列当事件发生时只会去唤醒一个任务,而事件组可以唤醒多个任务起到一个广播的作用。
2.信号量/队列是一个消耗性资源,即数据读走了则就减少,而事件组可以选择清除事件也可以选择保留事件。
3.事件组只能是起到一个同步的作用,并不能传递数据。

4.最重要的一点事件组可以实现多个任务之间的同步,队列/信号量则只能是两个任务之间的同步(看完源码你就懂了)。

  • 全局变量的区别

其实事件组相当于多个全局变量flag组成的,但是在操作系统中,事件组的优势明显。
1.全局变量使用在操作系统中存在被多个任务同时读写(前面的文章讲过为什么不能同时操作一个变量)的风险,则事件组它会直接禁止任务调度来规避风险。
2.使用全局变量需要自己去实现阻塞机制(成本太高)。
3.使用事件组能更方便的实现多任务之间的同步。

事件组的特点:
1.一个 32 位的事件集合(EventBits_t 类型的变量,实际可用与表示事件的只有 24 位,还有8位用于管理事件),其中每一位表示一种事件类型(0 表示该事件类型未发生、1 表示该事件类型已经发
生)。

2.事件仅用于同步,不提供数据传输功能。
3. 与信号量/队列不同设置事件组不会阻塞,即多次向任务设置同一事件等效于只设置一次。
4. 支持事件等待超时机制,即等待该事件类型(该事件还未发生)的任务会进入阻塞态。
5.事件获取的时候,有两个选择:1.逻辑或:任务所期望的事件中只要有任意一个事件发生,任务即可被唤醒。2.逻辑或:任务所期望的事件必须全部发生,任务才能被唤醒。

二、事件组源码分析

我们只需要搞清楚事件组是一个什么样的结构体,以及xEventGroupCreate()、xEventGroupSetBits()、xEventGroupWaitBits(),这三个函数的源码实现,还要搞明白上文提到的那8位是怎样管理事件组的。

1.事件组的创建

1.事件组结构体
在这里插入图片描述
uxEventBits:EventBits_t类型的变量,在32位平台下(stm32),其实就是一个32位的无符号整形,其中高8位是控制为,低24位则用来存储事件,每一位代表一个不同事件(1:事件发生 0:事件未发生)。
在这里插入图片描述xTasksWaitingForBits:是一个用来挂载等待事件发生的任务(阻塞机制)

一个变量,一条链表就是一个事件组,多么的简单。

2.动态创建事件组
在这里插入图片描述
创建事件组非常简单:
1.为事件组结构体分配内存
2.初始化事件组
3.初始化等待链表

2.等待事件函数 xEventGroupWaitBits()

为什么要先讲解 xEventGroupWaitBits()这个函数,我们先明白前面8位控制位的作用,等后面的xEventGroupSetBits()就很好理解了。

xEventGroupWaitBits()函数原型:
在这里插入图片描述
函数参数:
1.xEventGroup:事件组的句柄(传入事件组结构体的指针)。
2.uxBitsToWaitFor:等待的事件标志位,可以用逻辑或等待多个事件标志位,例如某任务想等待事件1和事件3,即(0x01 | 0x03)传入即可。
3.xClearOnExit:
pdTRUE:当 xEventGroupWaitBits()等待到满足任务唤醒的事件
时,系统将清除由形参 uxBitsToWaitFor 指定的事件标志位。
pdFALSE:不会清除由形参 uxBitsToWaitFor 指定的事件标志位。
(注意:这里清除只是清除该任务所期望的事件,并不会把24位都清零(因为其他任务所期望的事件与该任务无关))
4.xWaitForAllBits:
pdTRUE : 当形 参 uxBitsToWaitFor 指 定 的 位都 置 位 的 时 候 ,
xEventGroupWaitBits()才满足任务唤醒的条件,这也是“逻辑与”
等待事件,并且在没有超时的情况下返回对应的事件标志位的值。
pdFALSE:当形参 uxBitsToWaitFor 指定的位有其中任意一个置位
的时候,这也是常说的“逻辑或”等待事件,在没有超时的情况下
函数返回对应的事件标志位的值。

5.xTicksToWait:获取等待的阻塞时间

返回值:函数返回时,事件组中的事件标志位值

xEventGroupWaitBits()源码分析:


								 
 EventBits_t xEventGroupWaitBits( EventGroupHandle_t xEventGroup,
								  const EventBits_t uxBitsToWaitFor,
								  const BaseType_t xClearOnExit,
								  const BaseType_t xWaitForAllBits,
								  TickType_t xTicksToWait )
 {
	 EventGroup_t * pxEventBits = xEventGroup;
	 EventBits_t uxReturn, uxControlBits = 0;
	 BaseType_t xWaitConditionMet, xAlreadyYielded;
	 BaseType_t xTimeoutOccurred = pdFALSE;
 
	 /* 检查用户没有去尝试等待内核本身使用的位(高8位),并且至少请求了一个位(低24位)。*/
	 configASSERT( xEventGroup );
	 configASSERT( ( uxBitsToWaitFor & eventEVENT_BITS_CONTROL_BYTES ) == 0 );
	 configASSERT( uxBitsToWaitFor != 0 );
	 /* 这个不管 */
#if ( ( INCLUDE_xTaskGetSchedulerState == 1 ) || ( configUSE_TIMERS == 1 ) )
		 {
			 configASSERT( !( ( xTaskGetSchedulerState() == taskSCHEDULER_SUSPENDED ) && ( xTicksToWait != 0 ) ) );
		 }
#endif
     /* 挂起调度器 */
	 vTaskSuspendAll();
	 {
	     /* 取出该事件组的32位变量赋给uxCurrentEventBits */
		 const EventBits_t uxCurrentEventBits = pxEventBits->uxEventBits;
 
		 /* 检查是否已满足等待条件。 */
		 xWaitConditionMet = prvTestWaitCondition( uxCurrentEventBits, uxBitsToWaitFor, xWaitForAllBits );

		 /* 已满足等待条件 */
		 if( xWaitConditionMet != pdFALSE )
		 {
			 /* 已满足等待条件,任务无需阻塞(怕你之前设置了等待时间) */
			 uxReturn = uxCurrentEventBits;
			 xTicksToWait = ( TickType_t ) 0;
 
			 /* 如果请求清除等待位 */
			 if( xClearOnExit != pdFALSE )
			 {
			     /* 清除事件组中uxBitsToWaitFor设置的事件位 */
				 pxEventBits->uxEventBits &= ~uxBitsToWaitFor;
			 }
			 else
			 {
				 mtCOVERAGE_TEST_MARKER();
			 }
		 }
		 else if( xTicksToWait == ( TickType_t ) 0 )
		 {
			 /* 未满足等待条件,但没有阻塞时间指定,因此只需返回当前值。 */
			 uxReturn = uxCurrentEventBits;
			 xTimeoutOccurred = pdTRUE;
		 }
		 else
		 {
			 /*任务将被阻塞以等待其所需的位设置。uxControlBits变量
			   用于记住此调用 xEventGroupWaitBits()的指定行为(1.是否要清除 2.是逻辑与还是逻辑或) 
			 - 用于事件位取消阻止任务。 */
			 if( xClearOnExit != pdFALSE )
			 {
			     /* 若需要清除事件,将uxControlBits的第25位置1 */
				 uxControlBits |= eventCLEAR_EVENTS_ON_EXIT_BIT;
			 }
			 else
			 {
				 mtCOVERAGE_TEST_MARKER();
			 }
 
			 if( xWaitForAllBits != pdFALSE )
			 {
			     /* 若使用逻辑与,将uxControlBits的第26位置1 */
				 uxControlBits |= eventWAIT_FOR_ALL_BITS;
			 }
			 else
			 {
				 mtCOVERAGE_TEST_MARKER();
			 }
 
			 /* 1.将任务需要等待的事件位和事件控制位(即上文uxControlBits的值)或操作存储在任务的
			      xItemValue(原来辅助排序的值)中。
			    2.将任务添加至等待事件组的列表中。
			    3.将任务添加至延时列表中,任务进入阻塞态。
			 */
			 vTaskPlaceOnUnorderedEventList( &( pxEventBits->xTasksWaitingForBits ), ( uxBitsToWaitFor | uxControlBits ), xTicksToWait );

			 uxReturn = 0;
 
			 traceEVENT_GROUP_WAIT_BITS_BLOCK( xEventGroup, uxBitsToWaitFor );
		 }
	 }
	 /* 恢复调度器 */
	 xAlreadyYielded = xTaskResumeAll();
 
	 if( xTicksToWait != ( TickType_t ) 0 )
	 {
		 if( xAlreadyYielded == pdFALSE )
		 {
			 portYIELD_WITHIN_API();
		 }
		 else
		 {
			 mtCOVERAGE_TEST_MARKER();
		 }
 
		 /* 任务被阻塞以等待其所需的位被设置 - 此时已设置所需位或阻塞时间超时。
		     如果设置了所需的位,它们将存储在任务的事件列表项中,现在应该检索然后清除它们。 */
		 uxReturn = uxTaskResetEventItemValue();

		 /* 如果是阻塞时间超时导致的任务唤被醒,而不是任务所期待事件已发生了 */
		 if( ( uxReturn & eventUNBLOCKED_DUE_TO_BIT_SET ) == ( EventBits_t ) 0 )
		 {
			 taskENTER_CRITICAL();
			 {
				 /* 任务超时,只需返回当前事件位值。 */
				 uxReturn = pxEventBits->uxEventBits;
 
				 /* 事件位可能在此任务离开“已阻塞”状态并再次运行之间进行了更新
				    重新检测是否满足任务的等待条件*/
				 if( prvTestWaitCondition( uxReturn, uxBitsToWaitFor, xWaitForAllBits ) != pdFALSE )
				 {
					 if( xClearOnExit != pdFALSE )
					 {
						 pxEventBits->uxEventBits &= ~uxBitsToWaitFor;
					 }
					 else
					 {
						 mtCOVERAGE_TEST_MARKER();
					 }
				 }
				 else
				 {
					 mtCOVERAGE_TEST_MARKER();
				 }
 
				 xTimeoutOccurred = pdTRUE;
			 }
			 taskEXIT_CRITICAL();
		 }
		 else
		 {
			 /* 任务已唤醒,因为已设置位。 */
		 }
 
		 /* 任务被阻塞,因此可能已设置控制位. */
		 uxReturn &= ~eventEVENT_BITS_CONTROL_BYTES;
	 }
 
	 traceEVENT_GROUP_WAIT_BITS_END( xEventGroup, uxBitsToWaitFor, xTimeoutOccurred );
 
	 /* 不使用跟踪宏时防止编译器警告. */
	 ( void ) xTimeoutOccurred;
 
	 return uxReturn;
 }
 /*-----------------------------------------------------------*/

在这里插入图片描述
挑几个重点来讲:
1.如何检测任务已瞒住等待条件
在这里插入图片描述
uxBitsToWaitFor设置的是任务期望的事件,但满住任务的等待条件有两个选择,xWaitForAllBits==PdTURE则任务需要等待任务期望的事件全部发生,若xWaitForAllBits == PdFALSE 则任务只需等待其中任意一个事件发生就行。

2.当任务未满足等待的条件时,此时任务需要进入进入阻塞态了,但是进入阻塞态之前有个问题,之后任务该如何被唤醒?
所以我们要考虑几个问题:
1.我们至少需要保存该任务所期望的是那些事件(那些位)
2.需要保存任务到底是逻辑与还是逻辑或
3.满足条件后是否需要清除uxBitsToWaitFor设置的事件位
最后一个问题将这些东西保存在哪里?
答:保存在任务结构体中的xItemValue这个变量中

所以这就是事件组高8位的作用:
在这里插入图片描述
在这里插入图片描述
将这些信息保存在任务的xItemValue这个变量中,等下次调用xEventGroupSetBits()函数就知道该任务的是否已满足等待条件,以及满足等待条件后是否清除uxBitsToWaitFor设置的事件。
在这里插入图片描述
在这里插入图片描述
taskEVENT_LIST_ITEM_VALUE_IN_USE的作用就是提醒xItemValue已经被事件所使用,原来是用于任务的优先级辅助排序的。
在这里插入图片描述
有其他不懂的请看上面的整体代码解析

3.事件组置位函数 xEventGroupSetBits()

xEventGroupSetBits()函数原型:
在这里插入图片描述
函数参数:
xEventGroup:事件组句柄
uxBitsToSet:需要设置指定事件中的事件标志位。

返回值:函数返回时,事件组中的事件标志位值

EventBits_t xEventGroupSetBits( EventGroupHandle_t xEventGroup,
								const EventBits_t uxBitsToSet )
{
	ListItem_t * pxListItem, * pxNext;
	ListItem_t const * pxListEnd;
	List_t const * pxList;
	EventBits_t uxBitsToClear = 0, uxBitsWaitedFor, uxControlBits;
	EventGroup_t * pxEventBits = xEventGroup;
	BaseType_t xMatchFound = pdFALSE;

	/* 不允许用户区去设置内核使用的位本身。 */
	configASSERT( xEventGroup );
	configASSERT( ( uxBitsToSet & eventEVENT_BITS_CONTROL_BYTES ) == 0 );
    /* 获得该链表xTasksWaitingForBits的指针 */
	pxList = &( pxEventBits->xTasksWaitingForBits );
	/* 获得链表xTasksWaitingForBits的头结点 */
	pxListEnd = listGET_END_MARKER( pxList ); 
	/* 挂起调度器 */
	vTaskSuspendAll();
	{
		traceEVENT_GROUP_SET_BITS( xEventGroup, uxBitsToSet );
        /* 获取链表中第一个任务 */
		pxListItem = listGET_HEAD_ENTRY( pxList );

		/* 根据用户指定的 uxBitsToSet 设置事件标志位。 */
		pxEventBits->uxEventBits |= uxBitsToSet;

		/* 设置这个事件标志位可能是某个任务在等待的事件,就需要遍历
		  等待事件列表中的任务,看看这个事件是否与任务等待的事件匹配。
		  */
		while( pxListItem != pxListEnd )
		{
			pxNext = listGET_NEXT( pxListItem );
			/* 取出该任务xItemValue(在xEventGroupWaitBits()函数中存入的信息) */
			uxBitsWaitedFor = listGET_LIST_ITEM_VALUE( pxListItem );
			xMatchFound = pdFALSE;

			/* 事件组控制高8位存入uxControlBits变量中*/
			uxControlBits = uxBitsWaitedFor & eventEVENT_BITS_CONTROL_BYTES;
			/* 低24位存入uxBitsWaitedFor中 */
			uxBitsWaitedFor &= ~eventEVENT_BITS_CONTROL_BYTES;

			/* 此时uxControlBits就派上用场了 */
			
			if( ( uxControlBits & eventWAIT_FOR_ALL_BITS ) == ( EventBits_t ) 0 )
			{
				/* 任务所期望是事件中任意一个事件位被置位 */
				if( ( uxBitsWaitedFor & pxEventBits->uxEventBits ) != ( EventBits_t ) 0 )
				{
				    /* 任务满足了被唤醒的条件 */
					xMatchFound = pdTRUE;
				}
				else
				{
					mtCOVERAGE_TEST_MARKER();
				}
			}
			else if( ( uxBitsWaitedFor & pxEventBits->uxEventBits ) == uxBitsWaitedFor )
			{
				/* 任务所期望是事件中全部事件位被置位 */
			    /* 任务满足了被唤醒的条件 */
				xMatchFound = pdTRUE;
			}
			else
			{
				/* 需要设置所有位,但并非所有位都已设置。*/
			}

			if( xMatchFound != pdFALSE )
			{
				/* 位匹配,退出时是否应该清除位?*/
				if( ( uxControlBits & eventCLEAR_EVENTS_ON_EXIT_BIT ) != ( EventBits_t ) 0 )
				{
					uxBitsToClear |= uxBitsWaitedFor;
				}
				else
				{
					mtCOVERAGE_TEST_MARKER();
				}

				/* 在从事件列表中删除任务之前,将实际事件标志值存储在任务的事件列表项中。 
				这设置eventUNBLOCKED_DUE_TO_BIT_SET位,以便任务知道由于所需的位匹配而被唤醒,
				而不是因为它阻塞超时。(与xEventGroupWaitBits()函数相呼应)*/
				vTaskRemoveFromUnorderedEventList( pxListItem, pxEventBits->uxEventBits | eventUNBLOCKED_DUE_TO_BIT_SET );
			}

			/* 移至下一个任务。*/
			pxListItem = pxNext;
		}

		/*清除在控制字中设置eventCLEAR_EVENTS_ON_EXIT_BITbit时匹配的任何位. */
		pxEventBits->uxEventBits &= ~uxBitsToClear;
	}
	/* 恢复调度器 */
	( void ) xTaskResumeAll();

	return pxEventBits->uxEventBits;
}
/*-----------------------------------------------------------*/

在这里插入图片描述
讲解完xEventGroupWaitBits函数,xEventGroupSetBits就非常简单了,直接看代码注释就OK了。

不过这里需要提醒一下:
在这里插入图片描述
这个大的while循环是将所有挂载在xTasksWaitingForBits列表中的任务都遍历一遍,只要满足条件的任务即可唤醒,所以说事件组具有广播的作用,而且清除事件的时候是所有的任务需要清除事件的或。

4.任务同步函数xEventGroupSync

xEventGroupSync函数原型:
在这里插入图片描述
函数参数:
xEventGroup :事件组句柄
uxBitsToSet: 达到同步点后,要设置的事件标志
uxBitsToWaitFor :等待的事件标志
xTicksToWait: 等待的阻塞时间

函数返回值:
等待事件标志位成功,返回等待到的事件标志位
等待事件标志位失败,返回事件组中的事件标志位



EventBits_t xEventGroupSync( EventGroupHandle_t xEventGroup,
                             const EventBits_t uxBitsToSet,
                             const EventBits_t uxBitsToWaitFor,
                             TickType_t xTicksToWait )
{
    EventBits_t uxOriginalBitValue, uxReturn;
    EventGroup_t * pxEventBits = xEventGroup;
    BaseType_t xAlreadyYielded;
    BaseType_t xTimeoutOccurred = pdFALSE;

    configASSERT( ( uxBitsToWaitFor & eventEVENT_BITS_CONTROL_BYTES ) == 0 );
    configASSERT( uxBitsToWaitFor != 0 );
    #if ( ( INCLUDE_xTaskGetSchedulerState == 1 ) || ( configUSE_TIMERS == 1 ) )
        {
            configASSERT( !( ( xTaskGetSchedulerState() == taskSCHEDULER_SUSPENDED ) && ( xTicksToWait != 0 ) ) );
        }
    #endif

    vTaskSuspendAll();
    {
        uxOriginalBitValue = pxEventBits->uxEventBits;
         /* 调用位设置函数(逻辑与),用该函数唤醒阻塞的任务 */
        ( void ) xEventGroupSetBits( xEventGroup, uxBitsToSet );
        /* 所有任务都同步了 */
        if( ( ( uxOriginalBitValue | uxBitsToSet ) & uxBitsToWaitFor ) == uxBitsToWaitFor )
        {
            /* All the rendezvous bits are now set - no need to block. */
            uxReturn = ( uxOriginalBitValue | uxBitsToSet );

            /* 所有任务都同步后总是清除位 */
            pxEventBits->uxEventBits &= ~uxBitsToWaitFor;

            xTicksToWait = 0;
        }
        else
        {
            if( xTicksToWait != ( TickType_t ) 0 )
            {
                traceEVENT_GROUP_SYNC_BLOCK( xEventGroup, uxBitsToSet, uxBitsToWaitFor );

                /* 1.eventCLEAR_EVENTS_ON_EXIT_BIT:清除位
                   2.eventWAIT_FOR_ALL_BITS:逻辑与
                   3.任务添加至xTasksWaitingForBits列表
                   4.任务添加至延时链表,进入阻塞态*/
                vTaskPlaceOnUnorderedEventList( &( pxEventBits->xTasksWaitingForBits ), ( uxBitsToWaitFor | eventCLEAR_EVENTS_ON_EXIT_BIT | eventWAIT_FOR_ALL_BITS ), xTicksToWait );

                uxReturn = 0;
            }
            else
            {
                /* 未指定超时时间,只需返回当前事件位值。 */
                uxReturn = pxEventBits->uxEventBits;
                xTimeoutOccurred = pdTRUE;
            }
        }
    }
    xAlreadyYielded = xTaskResumeAll();

    if( xTicksToWait != ( TickType_t ) 0 )
    {
        if( xAlreadyYielded == pdFALSE )
        {
            portYIELD_WITHIN_API();
        }
        else
        {
            mtCOVERAGE_TEST_MARKER();
        }

        /* 任务被阻塞以等待其所需的位被设置 - 此时已设置所需位或阻塞时间超时。
		   如果设置了所需的位,它们将存储在任务的事件列表项中,现在应该检索然后清除它们。 */
        uxReturn = uxTaskResetEventItemValue();

        if( ( uxReturn & eventUNBLOCKED_DUE_TO_BIT_SET ) == ( EventBits_t ) 0 )
        {
            /* The task timed out, just return the current event bit value. */
            taskENTER_CRITICAL();
            {
                uxReturn = pxEventBits->uxEventBits;

                /* 尽管任务因为超时而到这里,
                  事件位可能在此任务离开阻状态到再次运行之间进行了更新。 
                  如果是这种情况,那么它需要在退出之前清除位。 */
                if( ( uxReturn & uxBitsToWaitFor ) == uxBitsToWaitFor )
                {
                    pxEventBits->uxEventBits &= ~uxBitsToWaitFor;
                }
                else
                {
                    mtCOVERAGE_TEST_MARKER();
                }
            }
            taskEXIT_CRITICAL();

            xTimeoutOccurred = pdTRUE;
        }
        else
        {
            /* The task unblocked because the bits were set. */
        }

        /* Control bits might be set as the task had blocked should not be
         * returned. */
        uxReturn &= ~eventEVENT_BITS_CONTROL_BYTES;
    }

    traceEVENT_GROUP_SYNC_END( xEventGroup, uxBitsToSet, uxBitsToWaitFor, xTimeoutOccurred );

    /* Prevent compiler warnings when trace macros are not used. */
    ( void ) xTimeoutOccurred;

    return uxReturn;
}
/*-----------------------------------------------------------*/

在这里插入图片描述
这个函数也简单:
唯一注意的是,任务的唤醒是在xEventGroupSync中调用了xEventGroupSetBits函数,当所期望的事件都发生了,则三个任务同步被唤醒。

三、总结

基础不牢地动山摇,底层都会了还怕不会运用嘛,只不过是熟练度的问题,加油加油加油!!!

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

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

相关文章

深入理解JVM读书笔记与实战_01_Java内存区域与内存溢出异常

文章目录 运行时数据区域问题引入 运行时数据区域 Java虚拟机在执行Java程序的过程中会把所管理的内存划分为若干个不同的数据区。运行时数据区包括了程序计数器、虚拟机栈、本地方法栈、方法区和堆。 程序计数器:程序计数器是线程私有的内存,用来记住…

vue:组件使用v-model实现2个组件间的数据双向绑定

一、需要实现的需求: 子组件输入框的数据发生改变,父组件的数据跟着实时改变; 父组件的数据发生改变,子组件的数据跟着实时改变。 二、实现思路: 1、(1)在父组件引入子组件。(2&…

CAN总线要点总结(CAN2.0A/B)

个人博客原文链接:CAN总线要点总结(CAN2.0A/B) 前言 工作也有几年了,在项目中也接触过几次CAN总线,但总是止步于会用即可,对于很多细节上的东西有时还是稀里糊涂的状态,这几天正好有点时间&am…

【亲测有效】pycharm不显示软件包

http://pypi.hustunique.com/ https://pypi.mirrors.ustc.edu.cn/ http://pypi.tuna.tsinghua.edu.cn/simple/ http://mirrors.aliyun.com/pypi/simple/ http://pypi.douban.com/simple/2023.5.13 亲测有效

单点登录系统:登录,登出,拦截器

什么是单点登录? 单点登录(Single Sign On),简称为 SSO,是目前比较流行的企业业务整合的解决方案之一。SSO 的定义是在多个应用系统中,用户只需要登录一次就可以访问所有相互信任的应用系统。 假设一个企业…

贪心算法(无规则)

目录 1.easy1.455. 分发饼干2.1005. K 次取反后最大化的数组和3.860. 柠檬水找零 2.medium1.序列问题1.376. 摆动序列2.738. 单调递增的数字 2.贪心解决股票问题1.122. 买卖股票的最佳时机 II 3.两个维度权衡问题1.135. 分发糖果*2.406. 根据身高重建队列(linklist,…

【WOA-LSTM】基于WOA优化 LSTM神经网络预测研究(Python代码实现)

💥💥💞💞欢迎来到本博客❤️❤️💥💥 🏆博主优势:🌞🌞🌞博客内容尽量做到思维缜密,逻辑清晰,为了方便读者。 ⛳️座右铭&a…

Neuron 2.4.0 发布:体验下一代工业物联网连接和管理

近日,EMQ 旗下的工业协议网关软件 Neuron 发布了最新的 2.4.0 版本。 该版本新增了包括 ABB COMLI 在内的四个南向驱动和一个北向应用,同时对现有的插件功能和 UI 进行了优化。 快速体验 Neuron 新版本 新增驱动插件满足不同场景需求 IEC61850 MMS 和 …

springboot项目如何优雅停机

文章目录 前言kill -9 pid的危害如何优雅的停机理论步骤优雅方式1、kill -15 pid 命令停机2、ApplicationContext close停机3、actuator shutdown 停机 前言 相信很多同学都会用Kill -9 PID来杀死进程,如果用在我们微服务项目里面,突然杀死进程会有什么…

计算机组成原理基础练习题第二章

1.下面四种语言中,()更适应网络环境 A、FORTRAN    B、JavaC、C        D、PASCAL 2.下列叙述中正确的是() A.终端是计算机硬件的一部分,好比电视中的小屏幕 B. ALU是代数逻辑单元的缩写 C.导航用计算机属于一般用途计算…

网络数据链路层介绍

文章目录 一、以太网二、以太网的帧格式三、局域网通信的原理四、ARP协议1.ARP协议的介绍2.ARP协议的工作流程3.ARP数据报格式 一、以太网 以太网并不是一种具体的网络,而是一种技术标准,它既包含了数据链路层的内容,也包含了一些物理层的内…

【数据分享】2023年全国A级景区数据(14847个景区)

我国的旅游景区,依据景区质量划分为五级,从高到低依次为5A、4A、3A、2A、A级旅游景区。我国旅游管理部门对于A级景区实行“有进有出”的动态管理,也就是A级景区的名单每年都在变!我们立方数据学社也一直在跟踪整理每年的A级景区数…

Android面试指南:说说你对组件化/模块化的理解

到现在组件化真的不是什么新鲜东西了,大公司都用的滚瓜烂熟,龙飞凤舞了,也就是现在部分中型项目和小项目在组件化的路上努力。所以同志们,组件化没玩过的,不熟悉的赶紧搞起来,说一点,你不会组件…

【牛客小白月赛72】BCD题

B、数数 比赛AC代码&#xff1a; #include <iostream> using namespace std;int t, n; int ans1;int check(int x) {int ans 0;for(int i 1; i < x/i; i)if(x%i 0){ans ;if(i ! x/i) ans;}ans1 ans;return ans1; } int main() {cin>> t;while(t--){cin&…

每日一练 | 华为认证真题练习Day44

1、如Display信息所示&#xff0c;当此交换机需要转发目的IAC地址为5489-98ec-f011的帧时&#xff0c;下面描述正确的是&#xff08;&#xff09;。 A. 交换机将会在除了收到该帧的端口之外的所有端口泛洪该帧 B. 交换机将会发送目标不可达的消息给源设备 C. 交换机在MAC地址…

easyExcel 与 POI 基础知识

文章目录 POI 与 easyExcel一、 了解1.1 Apache POI1.2 easyExcel 二、 准备工作2.1 Maven坐标2.2 Excel讲解 三、 Excel基本写操作&#xff08;导出Excel&#xff09;3.1 03 版本Excel导出操作3.2 07版本Excel导出操作3.3 大数据量的导出&#xff08;数据批量导入到磁盘&#…

含电动汽车的区域综合能源系统优化调度研究(Matlab代码实现)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…

django路由(多应用配置动态路由正则路由)

一、配置全局路由 在应用下&#xff0c;定义视图函数views.py from django.http import HttpResponse from django.shortcuts import render# Create your views here.def get_order(request):return HttpResponse("orders应用下的路由") 在项目的urls路由配置中&…

输入URL到显示界面的整个过程

以如下这个比较简单的网络拓扑模型作为例子&#xff0c;探究中间发生的整个过程&#xff1a; 1 HTTP 浏览器做的第一步工作就是要对 URL 进行解析&#xff0c;从而生成发送给 Web 服务器的请求信息。下图展示了一条长长的URL里各个元素代表什么&#xff1a; 所以整个长长的URL…

〖C++11〗智能指针详解

「前言」文章是关于C11的智能指针方面 「归属专栏」C嘎嘎 「笔者」枫叶先生(fy) 「座右铭」前行路上修真我 「枫叶先生有点文青病」 「每篇一句」 人生就是一列开往坟墓的列车&#xff0c; 路途上会有很多站&#xff0c; 很难有人自始至终陪着你走完。 当陪你的人要下车时&am…