FreeRTOSFreeRTOS列表和列表项

FreeRTOS列表和列表项

今天继续跟着正点原子学习FreeRTOS列表和列表项的内容。列表和列表项这个知识点用到了C语言链表的知识点。所以必须对C语言中的链表这个数据结构才能更好的理解这部分内容。TIPS:正点原子这节课内容讲的特别好,强烈推荐:第20讲列表和列表项简介

什么是列表和列表项

列表就是FreeRTOS中的数据结构,它被用来跟踪FreeRTOS中的任务。主要的列表包括:

  1. 就绪列表(Ready Lists):对于每个优先级,FreeRTOS都维护一个就绪列表。当任务处于就绪状态,能够运行,但由于CPU正被其他任务使用而无法立即执行时,该任务会被放入相应优先级的就绪列表中。当CPU可用时,调度器会从最高优先级的就绪列表中选择任务来执行。

  2. 阻塞列表(Blocked Lists):当任务等待某个事件(如信号量、互斥量或定时器)时,它会被放入阻塞列表。阻塞列表用于跟踪哪些任务正在等待资源变得可用。当等待的事件发生时,任务可以从阻塞列表移回就绪列表。

  3. 挂起列表(Suspended List):当任务被显式挂起(例如,调用了vTaskSuspend函数)时,它会被放入挂起列表。挂起的任务不会被调度器考虑运行,直到它们被显式地恢复(例如,调用了vTaskResume)。

  4. 延时列表(Delay Lists):当任务需要延迟执行或等待一段时间时,它会被放入延时列表。这通常用于实现非阻塞延时或定时功能。延时列表实际上分为两个:一个用于短期延时(当前延时列表),另一个用于长期延时(溢出延时列表)。这种分法帮助管理时间的回绕问题。

  5. 终止列表(Terminated List):某些FreeRTOS配置允许已删除或已结束的任务被放入终止列表,直到它们的任务控制块(TCB)被回收。这不是所有FreeRTOS配置都支持的功能。
    每个列表都是通过链表实现的,每个链表节点都包含了指向任务控制块(Task Control Block, TCB)的指针。TCB是FreeRTOS用来存储任务状态、堆栈指针和其他必要信息的结构。
    FreeRTOS的调度器会根据这些列表及其内任务的状态和优先级来决定哪个任务应该获得CPU时间。通过这种方式,FreeRTOS实现了任务的优先级调度和时间共享调度。与列表有关的内容都在list.c和list.h中。在list.h中定义一个叫做List_t的结构体。下面我们来看一下:

typedef struct xLIST
{
    listFIRST_LIST_INTEGRITY_CHECK_VALUE      /*< Set to a known value if configUSE_LIST_DATA_INTEGRITY_CHECK_BYTES is set to 1. */
    volatile UBaseType_t uxNumberOfItems;
    ListItem_t * configLIST_VOLATILE pxIndex; /*< Used to walk through the list.  Points to the last item returned by a call to listGET_OWNER_OF_NEXT_ENTRY (). */
    MiniListItem_t xListEnd;                  /*< List item that contains the maximum possible item value meaning it is always at the end of the list and is therefore used as a marker. */
    listSECOND_LIST_INTEGRITY_CHECK_VALUE     /*< Set to a known value if configUSE_LIST_DATA_INTEGRITY_CHECK_BYTES is set to 1. */
} List_t;

在FreeRTOS中,List_t结构体是用于实现各种任务列表(如就绪列表、阻塞列表等)的核心数据结构。下面是List_t结构体各个成员的作用解释:

  1. listFIRST_LIST_INTEGRITY_CHECK_VALUE:这是一个可选的完整性检查值,仅在configUSE_LIST_DATA_INTEGRITY_CHECK_BYTES设置为1时使用。它的目的是帮助开发者检测内存损坏或不正确的列表操作。这个值通常被设置为一个特定的常量,以便在运行时检查列表的完整性。

  2. volatile UBaseType_t uxNumberOfItems:这个成员变量用来记录列表中项目的数量。它是volatile类型的,因为它可能会在中断服务例程和普通任务代码之间共享,确保编译器在每次访问时都会从内存中重新读取其值,而不是使用可能已经过时的寄存器副本。

  3. ListItem_t * configLIST_VOLATILE pxIndex:这是一个指向列表中某个项目的指针,用作遍历列表的索引。configLIST_VOLATILE是一个宏,用于确保在特定的编译器或配置下,pxIndex以正确的方式被视为volatilepxIndex通常用于通过listGET_OWNER_OF_NEXT_ENTRY()宏来遍历列表,每次调用都会返回列表中的下一个元素。

  4. MiniListItem_t xListEnd:这个成员是列表的一个哨兵(或标记)元素,表示列表的末尾。xListEndxItemValue通常被设置为最大可能的值,以确保它总是位于列表的末尾。这个哨兵元素使得列表操作(如插入和删除)可以在不需要特殊情况处理的情况下进行,因为列表始终至少包含一个元素(即xListEnd)。

  5. listSECOND_LIST_INTEGRITY_CHECK_VALUE:这是第二个可选的完整性检查值,其作用与listFIRST_LIST_INTEGRITY_CHECK_VALUE相同,提供额外的完整性检查。这也只有在configUSE_LIST_DATA_INTEGRITY_CHECK_BYTES设置为1时才会启用。
    至于第四点为啥要有一个哨兵元素,如果有链表操作知识的基础,在没有哨兵元素的链表中,插入和删除操作需要检查列表是否为空,以及操作是否发生在列表的开头或末尾。这增加了实现的复杂性。使用哨兵元素后,这些特殊情况的处理被统一化,因为列表永远不会为空(至少有一个哨兵元素),且不需要单独处理列表的末尾。统一插入和删除操作:哨兵元素确保每个插入和删除操作都有一个统一的前驱节点和后继节点,无论操作发生在列表的哪个位置。这意味着代码可以使用相同的逻辑来处理所有情况。

在这里插入图片描述
所以列表中忽略那两个检查元素,所有的具体的成员如上图所示。
接下来让我们来看下列表项和迷你列表项。其中迷你列表项就是xlistend,也就是列表标记的哨兵元素。首先来看下列表项和迷你列表项所拥有成员的差异,具体差异如下图所示。
在这里插入图片描述
可以看出迷你列表项只有三个成员。xMINI_LIST_ITEM结构体是FreeRTOS中用来表示列表中的一个元素或节点的数据结构。它是ListItem_t的简化版本,通常用于实现哨兵元素(如xListEnd),以简化列表操作。下面是xMINI_LIST_ITEM结构体每个成员的详细解释:

. configLIST_VOLATILE TickType_t xItemValue:这个成员变量用于存储元素的排序值或优先级。TickType_t通常是一个无符号整型,用于表示时间或者计数。在FreeRTOS中,列表经常根据这个值进行排序,例如,用于管理延时任务的列表就是按照xItemValue的值(表示唤醒时间)进行排序的。configLIST_VOLATILE是一个宏,用于确保在特定的编译器或配置下,xItemValue以正确的方式被视为volatile。这是因为xItemValue可能会在中断服务例程中被修改。

struct xLIST_ITEM * configLIST_VOLATILE pxNext:这是一个指向列表中下一个元素的指针。它使得列表可以以链表的形式进行遍历。configLIST_VOLATILE确保pxNext在需要时被正确处理为volatile,以适应可能的并发修改。

struct xLIST_ITEM * configLIST_VOLATILE pxPrevious:这是一个指向列表中前一个元素的指针。它的存在使得列表成为一个双向链表,允许从任一方向遍历列表。这对于某些操作,如从列表中删除一个元素,是非常有用的,因为可以直接访问前一个元素,从而简化了指针的重新链接过程。同样,configLIST_VOLATILE确保在特定配置下,pxPrevious被正确地视为volatile
至于为啥非要整出来一个迷你列表项,而不直接全部都用列表项。这可能会让人疑惑它们之间的区别以及为什么需要两种不同的结构体。主要区别通常在于它们的用途和设计意图,而不仅仅是结构体成员本身。下面是创建迷你列表项的原因。

1. 简化和优化

xMINI_LIST_ITEMListItem_t的简化版本。虽然它们的成员变量可能相似,xMINI_LIST_ITEM通常用于特定场景,如作为哨兵节点(xListEnd)或在不需要完整ListItem_t功能的场合。这种简化有助于减少内存占用和提高代码效率,尤其是在资源受限的嵌入式系统中。

2. 特定用途

xMINI_LIST_ITEM通常用于实现列表的哨兵节点,这是一个始终存在于列表末尾的特殊节点,用以简化列表操作逻辑。而ListItem_t则用于表示实际的列表数据项。这种区分使得代码更加清晰,逻辑更加简单。

3. 减少复杂性

在某些情况下,列表项可能不需要ListItem_t提供的所有功能。例如,哨兵节点不需要存储额外的数据(如所属任务的指针或其他用户定义数据)。在这种情况下,使用更简单的xMINI_LIST_ITEM可以减少实现的复杂性,同时保持足够的功能性。

4. 内存效率

在资源受限的嵌入式系统中,每个字节的内存都很宝贵。xMINI_LIST_ITEM由于其简化的设计,占用的内存可能比ListItem_t少。在只需要基本链表功能(如哨兵节点)时,使用xMINI_LIST_ITEM可以节省宝贵的系统资源。
那了解了迷你列表项和成员变量和其作用后下面我们来了解下,列表项中各个成员的作用。
具体代码如下:

struct xLIST_ITEM
{
    listFIRST_LIST_ITEM_INTEGRITY_CHECK_VALUE           /*< Set to a known value if configUSE_LIST_DATA_INTEGRITY_CHECK_BYTES is set to 1. */
    configLIST_VOLATILE TickType_t xItemValue;          /*< The value being listed.  In most cases this is used to sort the list in ascending order. */
    struct xLIST_ITEM * configLIST_VOLATILE pxNext;     /*< Pointer to the next ListItem_t in the list. */
    struct xLIST_ITEM * configLIST_VOLATILE pxPrevious; /*< Pointer to the previous ListItem_t in the list. */
    void * pvOwner;                                     /*< Pointer to the object (normally a TCB) that contains the list item.  There is therefore a two way link between the object containing the list item and the list item itself. */
    struct xLIST * configLIST_VOLATILE pxContainer;     /*< Pointer to the list in which this list item is placed (if any). */
    listSECOND_LIST_ITEM_INTEGRITY_CHECK_VALUE          /*< Set to a known value if configUSE_LIST_DATA_INTEGRITY_CHECK_BYTES is set to 1. */
};
typedef struct xLIST_ITEM ListItem_t;                   /* For some reason lint wants this as two separate definitions. */

ListItem_t(或struct xLIST_ITEM)是FreeRTOS中用于构建双向链表的核心数据结构之一。它比xMINI_LIST_ITEM更为复杂,提供了更多的功能和灵活性。

  1. listFIRST_LIST_ITEM_INTEGRITY_CHECK_VALUE: 当configUSE_LIST_DATA_INTEGRITY_CHECK_BYTES设置为1时,此成员被设置为一个已知的值,用于运行时检查列表项的完整性。这有助于开发者识别内存损坏或不当的列表操作,增加了代码的健壮性。

  2. configLIST_VOLATILE TickType_t xItemValue: 这是列表项的值,通常用于排序列表。在大多数情况下,列表是按照xItemValue的升序排列的。例如,FreeRTOS的延时任务列表就是根据任务唤醒时间(用xItemValue表示)进行排序的。TickType_t是一个基于配置的类型,通常为无符号整型,代表时间或计数。configLIST_VOLATILE确保此成员在需要时被视为volatile,适用于可能在中断服务例程中修改的场景。

  3. struct xLIST_ITEM * configLIST_VOLATILE pxNext: 指向链表中下一个ListItem_t的指针。这使得可以从当前列表项向后遍历整个列表。

  4. struct xLIST_ITEM * configLIST_VOLATILE pxPrevious: 指向链表中前一个ListItem_t的指针。这使得可以从当前列表项向前遍历整个列表,是双向链表结构的关键组成部分。

  5. void * pvOwner: 指向包含此列表项的对象的指针。通常,这个指针指向一个任务控制块(TCB),但也可以指向其他使用列表项的数据结构。这实现了对象和其所属列表项之间的双向链接。

  6. struct xLIST * configLIST_VOLATILE pxContainer: 指向包含此列表项的列表的指针。这个成员变量使得可以从列表项访问其所属的列表,进而实现诸如移除列表项或调整列表结构等操作。

  7. listSECOND_LIST_ITEM_INTEGRITY_CHECK_VALUE: 类似于listFIRST_LIST_ITEM_INTEGRITY_CHECK_VALUE,这是另一个完整性检查值,当configUSE_LIST_DATA_INTEGRITY_CHECK_BYTES设置为1时使用。它位于结构体的末尾,为列表项提供额外的内存完整性验证。
    上面已经介绍完了列表、列表项、迷你列表项的结构以及成员变量,接下来来看下如何形成双向链表。这一点视频里面讲的很好。直接来看视频这个地方:
    在这里插入图片描述
    其实双向环形链表就是三个人拉成一个圈一样,小明的右手指向小黑的左手,小明的左手指向小红的右手。小红、小黑的过程类似。依次形成了双向环形链表。
    比如这里列表有两个列表项成员,如下图所示:详细PPT可以看正点原子开源网中课件与源码:

在这里插入图片描述
我们从这个图可以看出来,列表项1相当于为头,列表项2为第二项,末尾列表项就是在末尾。所以末尾列表项前一个就是列表项2,末尾列表项下一个就是头,因为是双向环形链表。也就是列表项1,列表项2前一个就是就是列表项1,所以指向列表项1,列表项2下一个是末尾列表项,而列表一前一个就是末尾列表项,下一个就是列表项2.具体指向关系就是上图所示。

列表和列表项API函数

列表和列表项API函数
我们去FreeRTOS的具体工程去看上述函数的具体实现代码:

void vListInitialise( List_t * const pxList )

void vListInitialise( List_t * const pxList )
{
    /* The list structure contains a list item which is used to mark the
     * end of the list.  To initialise the list the list end is inserted
     * as the only list entry. */
    pxList->pxIndex = ( ListItem_t * ) &( pxList->xListEnd ); /*这行代码将链表的索引(pxIndex)设置为链表结束标志(xListEnd)的地址。在FreeRTOS中,每个链表都有一个特殊的列表项作为链表的结束标志,这个特殊的列表项不存储任何用户数据,它的主要目的是标记链表的末尾。 */

    listSET_FIRST_LIST_ITEM_INTEGRITY_CHECK_VALUE( &( pxList->xListEnd ) );

    /* 这行代码设置列表结束标志的xItemValue为portMAX_DELAY,这是一个非常大的值,确保结束标志始终位于链表的最末端。 */
    pxList->xListEnd.xItemValue = portMAX_DELAY;

    /* The list end next and previous pointers point to itself so we know
     * when the list is empty. */
    pxList->xListEnd.pxNext = ( ListItem_t * ) &( pxList->xListEnd );     /*lint !e826 !e740 !e9087 The mini list structure is used as the list end to save RAM.  This is checked and valid. */
    pxList->xListEnd.pxPrevious = ( ListItem_t * ) &( pxList->xListEnd ); /*这两行代码使列表结束标志的pxNext和pxPrevious指针都指向自身,这样做的目的是在链表为空时,能够通过检查这些指针来快速识别。 */

    /* 如果配置没有使用迷你列表项(configUSE_MINI_LIST_ITEM为0),则将xListEnd的pvOwner和pxContainer字段初始化为NULL,并设置完整性检查值。这是为了确保列表结束标志作为一个完整的列表项符合预期的数据完整性要求 */
    #if ( configUSE_MINI_LIST_ITEM == 0 )
    {
        pxList->xListEnd.pvOwner = NULL;
        pxList->xListEnd.pxContainer = NULL;
        listSET_SECOND_LIST_ITEM_INTEGRITY_CHECK_VALUE( &( pxList->xListEnd ) );
    }
    #endif

    pxList->uxNumberOfItems = ( UBaseType_t ) 0U;//这行代码将链表中的项数初始化为0,表示链表是空的。

    /* Write known values into the list if
     * configUSE_LIST_DATA_INTEGRITY_CHECK_BYTES is set to 1. */
    listSET_LIST_INTEGRITY_CHECK_1_VALUE( pxList );
    listSET_LIST_INTEGRITY_CHECK_2_VALUE( pxList );
}

void vListInitialiseItem( ListItem_t * const pxItem )

void vListInitialiseItem( ListItem_t * const pxItem )
{
    /* Make sure the list item is not recorded as being on a list. */
    pxItem->pxContainer = NULL;

    /* Write known values into the list item if
     * configUSE_LIST_DATA_INTEGRITY_CHECK_BYTES is set to 1. */
    listSET_FIRST_LIST_ITEM_INTEGRITY_CHECK_VALUE( pxItem );
    listSET_SECOND_LIST_ITEM_INTEGRITY_CHECK_VALUE( pxItem );
}

void vListInsertEnd( List_t * const pxList, ListItem_t * const pxNewListItem )

void vListInsertEnd( List_t * const pxList,
                     ListItem_t * const pxNewListItem )
{
    ListItem_t * const pxIndex = pxList->pxIndex;/*这行代码定义了一个指
    向ListItem_t的常量指针pxIndex,并将其初始化为指向pxList中的pxIndex成员。pxIndex通常用于标记链表的
    起始位置或用于遍历链表。*/

    /* Only effective when configASSERT() is also defined, these tests may catch
     * the list data structures being overwritten in memory.  They will not catch
     * data errors caused by incorrect configuration or use of FreeRTOS. */
    listTEST_LIST_INTEGRITY( pxList );
    listTEST_LIST_ITEM_INTEGRITY( pxNewListItem );

    /* Insert a new list item into pxList, but rather than sort the list,
     * makes the new list item the last item to be removed by a call to
     * listGET_OWNER_OF_NEXT_ENTRY(). */
    pxNewListItem->pxNext = pxIndex;
    pxNewListItem->pxPrevious = pxIndex->pxPrevious;/*由于要将这个列表项插入到pxindex的前一个。
    由于是双向环形链表,所以相当于该列表项伸出来2只手,一个拉前面,一个拉后面*/

    /* Only used during decision coverage testing. */
    mtCOVERAGE_TEST_DELAY();

    pxIndex->pxPrevious->pxNext = pxNewListItem;
    pxIndex->pxPrevious = pxNewListItem;/*新列表项已经伸出来2只手,那么前面的是不是也要伸出来一只手连接到新的列表项。
    那新的列表项后面那个是不是也要往前面伸出来一只手与之连接才能形成双向环形链表*/

    /* Remember which list the item is in. */
    pxNewListItem->pxContainer = pxList;

    ( pxList->uxNumberOfItems )++;
}

上面这段函数如果对链表操作熟悉的话,就其实是把pxNewListItem 插入pxIndex前面的链表操作。

void vListInsert( List_t * const pxList, ListItem_t * const pxNewListItem )

/*-----------------------------------------------------------*/

void vListInsert( List_t * const pxList,
                  ListItem_t * const pxNewListItem )
{
    ListItem_t * pxIterator;
    const TickType_t xValueOfInsertion = pxNewListItem->xItemValue;

    /* Only effective when configASSERT() is also defined, these tests may catch
     * the list data structures being overwritten in memory.  They will not catch
     * data errors caused by incorrect configuration or use of FreeRTOS. */
    listTEST_LIST_INTEGRITY( pxList );
    listTEST_LIST_ITEM_INTEGRITY( pxNewListItem );

    /* 如果xValueOfInsertion的值等于portMAX_DELAY,这通常表示一个特殊的值,
    用于将新的列表项插入到列表的末尾。因此,pxIterator被设置为指向列表末尾前的项(pxList->xListEnd.pxPrevious)。
    如果xValueOfInsertion不等于portMAX_DELAY,则通过一个循环找到新列表项应
    该插入的位置。循环遍历列表,直到找到一个其xItemValue大于xValueOfInsertion的项。pxIterator在这个过程中用于遍历列表。 */
    if( xValueOfInsertion == portMAX_DELAY )
    {
        pxIterator = pxList->xListEnd.pxPrevious;
    }
    else
    {
		/*该循环用来寻找插入的正确位置,这个函数插入是按照数值升序去插入的。首先循环的初始值就是哨兵节点的地址,
		由于要找到合适的插入位置,也就是终止循环的条件是pxIterator->pxNext->xItemValue > xValueOfInsertion。
		所以此时的pxIterator就是要插入位置的前一个节点。
		*/
        for( pxIterator = ( ListItem_t * ) &( pxList->xListEnd ); pxIterator->pxNext->xItemValue <= xValueOfInsertion; pxIterator = pxIterator->pxNext ) /*lint !e826 !e740 !e9087 The mini list structure is used as the list end to save RAM.  This is checked and valid. *//*lint !e440 The iterator moves to a different value, not xValueOfInsertion. */
        {
            /* There is nothing to do here, just iterating to the wanted
             * insertion position. */
        }
    }
	/*这里更好的理解就是理解为手拉手操作,要更新插入新的人前后指向位置,以及新的人前面的人指向后的位置。还有新的人后面的人指向前面的位置,这样才能形成双向链表。这里用图其实更好理解*/
    pxNewListItem->pxNext = pxIterator->pxNext;
    pxNewListItem->pxNext->pxPrevious = pxNewListItem;
    pxNewListItem->pxPrevious = pxIterator;
    pxIterator->pxNext = pxNewListItem;

    /* Remember which list the item is in.  This allows fast removal of the
     * item later. */
    pxNewListItem->pxContainer = pxList;

    ( pxList->uxNumberOfItems )++;
}
/*-----------------------------------------------------------*/

这个函数完成链表操作我用图大概画了下:
在这里插入图片描述
这样理解为牵手更容易去理解。
下面来看最后一个函数也就是删除链表中某个节点。删除要进行的操作就是

UBaseType_t uxListRemove( ListItem_t * const pxItemToRemove )
{
/* The list item knows which list it is in.  Obtain the list from the list
 * item. */
    List_t * const pxList = pxItemToRemove->pxContainer;

    pxItemToRemove->pxNext->pxPrevious = pxItemToRemove->pxPrevious;
    pxItemToRemove->pxPrevious->pxNext = pxItemToRemove->pxNext;

    /* Only used during decision coverage testing. */
    mtCOVERAGE_TEST_DELAY();

    /* Make sure the index is left pointing to a valid item. */
    if( pxList->pxIndex == pxItemToRemove )
    {
        pxList->pxIndex = pxItemToRemove->pxPrevious;
    }
    else
    {
        mtCOVERAGE_TEST_MARKER();
    }

    pxItemToRemove->pxContainer = NULL;
    ( pxList->uxNumberOfItems )--;

    return pxList->uxNumberOfItems;
}

在这里插入图片描述

实际要完成的操作就是把1,2,3,4都给断掉,变成蓝色的。
所以要完成代码的操作就是下面这个:
1:pxItemToRemove->pxNext->pxPrevious = pxItemToRemove->pxPrevious;
2:pxItemToRemove->pxPrevious->pxNext = pxItemToRemove->pxNext;
在这里插入图片描述

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

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

相关文章

08 | Swoole 源码分析之 Timer 定时器模块

原文首发链接&#xff1a;Swoole 源码分析之 Timer 定时器模块 大家好&#xff0c;我是码农先森。 引言 Swoole 中的毫秒精度的定时器。底层基于 epoll_wait 和 setitimer 实现&#xff0c;数据结构使用最小堆&#xff0c;可支持添加大量定时器。 在同步 IO 进程中使用 seti…

数据库系统概论(超详解!!!)第三节 关系数据库标准语言SQL(Ⅵ)

1.空值的处理 空值就是“不知道”或“不存在”或“无意义”的值。 一般有以下几种情况&#xff1a; 该属性应该有一个值&#xff0c;但目前不知道它的具体值 &#xff1b;该属性不应该有值 &#xff1b;由于某种原因不便于填写。 1.空值的产生 空值是一个很特殊的值&#x…

什么牌子开放式耳机好用?优选五大高分好物真诚分享

对于习惯长时间佩戴耳机的朋友来说&#xff0c;入耳式耳机固然能够提供较优质的音质体验。但是&#xff0c;由于其较为封闭的设计以及对耳洞的压迫&#xff0c;舒适感较差&#xff0c;长时间佩戴可能会对听力造成一定的影响。因此&#xff0c;开放式耳机的出现为音乐发烧友们提…

青风环境带您了解2024第13届生物发酵展

参展企业介绍 浙江青风环境股份有限公司创立于1998年&#xff0c;是一家集科研、生产及贸易为一体的高新技术企业。公司座落于浙江省丽水市水阁工业区&#xff0c;占地面积120亩&#xff0c;建筑面积近11万平方米&#xff0c;年产值可达20亿元&#xff0c;建有标准的冷&#x…

回归预测 | Matlab实现WOA-GPR鲸鱼算法优化高斯过程回归多变量回归预测

回归预测 | Matlab实现WOA-GPR鲸鱼算法优化高斯过程回归多变量回归预测 目录 回归预测 | Matlab实现WOA-GPR鲸鱼算法优化高斯过程回归多变量回归预测预测效果基本介绍程序设计参考资料 预测效果 基本介绍 Matlab实现WOA-GPR鲸鱼算法优化高斯过程回归多变量回归预测 1.Matlab实现…

期货学习笔记-MACD指标学习2

MACD底背离把握买入多单的技巧 底背离的概念及特征 底背离指的是MACD指标与价格低点之间的对比关系&#xff0c;这里需要明白的是MACD指标的涨跌动能和价格形态衰竭形态之间的关系&#xff0c;如果市场价格创新低而出现衰竭形态同时也有底背离形态的出现&#xff0c;此时下跌…

Github项目推荐-ChatGPT-Admin-Web

项目地址 https://github.com/AprilNEA/ChatGPT-Admin-Web 项目简介 通过api接入大模型&#xff0c;并基于此封装了一层用户管理的功能&#xff0c;适合团队内使用。 项目截图

【Easy云盘 | 第三篇】登录注册模块上篇(获取验证码、发送邮箱验证码、登录、注册、重置密码)基于AOP实现参数校验

文章目录 4.2登录注册模块设计4.2.1获取验证码&#xff08;1&#xff09;思路&#xff08;2&#xff09;接口&#xff08;3&#xff09;controller层&#xff08;4&#xff09;CreateImageCodeUtils工具类&#xff08;5&#xff09;测试结果 4.2.2发送邮箱验证码&#xff08;1&…

Chapter 1 Basic Concepts of Communication and Communication Systems

1.1 The Concept of Communication communication【通信】:It is the process of using signals to transmit messages containing information in space. To put it simply, communication is the spatial transmission of information【信息的空间传递】Information【信息】…

LeetCode-46. 全排列【数组 回溯】

LeetCode-46. 全排列【数组 回溯】 题目描述&#xff1a;解题思路一&#xff1a;回溯。回溯三部曲解题思路二&#xff1a;0解题思路三&#xff1a;0 题目描述&#xff1a; 给定一个不含重复数字的数组 nums &#xff0c;返回其 所有可能的全排列 。你可以 按任意顺序 返回答案…

信息收集之内网渗透(二)

★★免责声明★★ 文章中涉及的程序(方法)可能带有攻击性&#xff0c;仅供安全研究与学习之用&#xff0c;读者将信息做其他用途&#xff0c;由Ta承担全部法律及连带责任&#xff0c;文章作者不承担任何法律及连带责任。 0、前言 本文主要是一些命令的集合&#xff0c;会比较枯…

PCL 点到三角形的距离(3D)

文章目录 一、简介二、实现代码三、实现效果参考资料一、简介 给定三角形ABC和点P,设Q为描述ABC上离P最近的点。求Q的一个方法:如果P在ABC内,那么P的正交投影点就是离P最近的点Q。如果P投影在ABC之外,最近的点则必须位于它的一条边上。在这种情况下,Q可以通过计算线段AB、…

【算法】动态规划练习(一)

目录 1137. 第 N 个泰波那契数 分析 代码 面试题 08.01. 三步问题 分析 代码 746. 使用最小花费爬楼梯 分析 代码 泰波那契序列 Tn 定义如下&#xff1a; T0 0, T1 1, T2 1, 且在 n > 0 的条件下 Tn3 Tn Tn1 Tn2 给你整数 n&#xff0c;请返回第 n 个泰波…

Redis: 持久化

文章目录 一、RDB持久化1、概念2、执行时机&#xff08;1&#xff09; 执行save命令&#xff08;2&#xff09;执行bgsave命令&#xff08;3&#xff09;Redis停机时&#xff08;4&#xff09;触发RDB条件 3、原理4、小结 二、AOF持久化1、概念2、AOF配置3、AOF文件重写4、RDB与…

中文地址分词器源码阅读(jiedi)

文章目录 structure.p文件pd.read_excelenumerate思维导图核心源码讲解jiedi.pytrain.py 总结 structure 点击左边的Structure按钮就如Structure界面。从Structure我们可以看出当前代码文件中有多少个全局变量、函数、类以及类中有多少个成员变量和成员函数。 其中V图标表示全…

HFSS仿真环形耦合器学习笔记

HFSS仿真环形耦合器学习笔记 文章目录 HFSS仿真环形耦合器学习笔记1、 理论基础2、 设计分析3、 仿真验证1、 求解器设置2、 建模3、 激励方式设置4、 边界条件设置5、 扫频设置6、 设计检查&#xff0c;仿真分析7、 数据后处理 1、 理论基础 环形定向耦合器的结构示意图如图所…

三分钟带你了解,可重构柔性装配生产线

产品个性化时代&#xff0c;产品小批量、多批次&#xff0c;行业常用高柔性的人-机混合装配线实现跨品类产品装配&#xff0c;但产品的装配质量一致性差、效率低成为行业痛点。富唯智能联合清华大学提出了可重构柔性装配方法和技术&#xff0c;实现跨品类产品的数控自动化装配。…

Spring 源码学习笔记(一)之搭建源码环境

前言 一直以来对 Spring 源码的理解不够全面&#xff0c;也不成条理&#xff0c;只是对其中的某小部分比较了解&#xff0c;所以从今天开始要重新系统学习 Spring 的源码了。 搭建源码环境 首先需要说明的是&#xff0c;源码环境并不是必须的&#xff0c;搭建源码环境唯一的好…

代码随想录算法训练营第四十六天 |139. 单词拆分 、卡码网56. 携带矿石资源

代码随想录算法训练营第四十六天 |139. 单词拆分 、卡码网56. 携带矿石资源 139. 单词拆分题目解法 卡码网56. 携带矿石资源题目解法 背包总结感悟 139. 单词拆分 题目 解法 题解链接 1. class Solution { public:bool wordBreak(string s, vector<string>& wordD…

JUC_1

进程 概述 进程&#xff1a;程序是静止的&#xff0c;进程实体的运行过程就是进程&#xff0c;是系统进行资源分配的基本单位 进程的特征&#xff1a;并发性、异步性、动态性、独立性、结构性 线程&#xff1a;线程是属于进程的&#xff0c;是一个基本的 CPU 执行单元&#x…