HAL库版FreeRTOS(下)

news2024/12/25 0:05:28

目录

  • FreeRTOS 事件标志组
    • FreeRTOS 事件标志组简介
    • FreeRTOS 事件标志组相关API 函数
    • FreeRTOS 事件标志组实验
      • 功能设计
      • 软件设计
      • 下载验证
  • FreeRTOS 任务通知
    • FreeRTOS 任务通知简介
      • 任务通知的优势
      • 任务通知的缺点
    • FreeRTOS 任务通知相关API 函数

FreeRTOS 事件标志组

事件标志组与信号量一样属于任务间同步的机制,但是信号量一般用于任务间的单事件同
步,对于任务间的多事件同步,仅使用信号量就显得力不从心了。FreeRTOS 提供的事件标志组
可以很好的处理多事件情况下的任务同步。本章就来学习FreeRTOS 中事件标志组的相关内容。

FreeRTOS 事件标志组简介

  1. 事件标志
    事件标志是一个用于指示事件是否发生的布尔值,一个事件标志只有0 或1 两种状态,
    FreeRTOS 将多个事件标志储存在一个变量类型为EventBits_t 变量中,这个变量就是事件组。
  2. 事件组
    事件组是一组事件标志的集合,一个事件组就包含了一个EventBites_t 数据类型的变量,
    变量类型EventBits_t 的定义如下所示:
typedef TickType_t EventBits_t;#
if (configUSE_16_BIT_TICKS == 1)
    typedef uint16_t TickType_t;#
else
    typedef uint32_t TickType_t;#
endif# define configUSE_16_BIT_TICKS 0

从上面可以看出,EventBits_t 实际上是一个16 位或32 位无符号的数据类型。当
configUSE_16_BIT_TICKS 配置为0 时,EventBits_t 是一个32 位无符号的数据类型;当
configUSE_16_BIT_TICKS 配置为1 时,EventBits_t 是一个16 为无符号的数据类型。在本套教
程的所有配套例程中,都将配置项configUSE_16_BIT_TICKS 配置为0,因此本文就以
EventBits_t 为32 位无符号数据类型为例进行讲解,对于另外一种情况,也是大同小异的。
虽然说使用了32 位无符号的数据类型变量来存储事件标志,但这并不意味着,一个
EventBits_t 数据类型的变量能够存储32 个事件标志,FreeRTOS 将这个EventBits_t 数据类型的
变量拆分成两部分,其中低24 位[23:0] (configUSE_16_BIT_TICKS 配置位1 时,是低8 位[7:0])
用于存储事件标志,而高8 位[31:24](configUSE_16_BIT_TICKS 配置位1 时,依然是高8 位
[15:8])用作存储事件标志组的一些控制信息,也就是说一个事件组最多可以存储24 个事件标
志。EventBits_t 数据类型变量的位使用情况如下图所示:
在这里插入图片描述
从上图中可以看到,变量中低24 位中的每一位都是一个事件标志,当某一位被置一时,就
表示这一位对应的事件发生了。

FreeRTOS 事件标志组相关API 函数

FreeRTOS 提供了事件标志组的一些相关操作函数,如下表所示:
在这里插入图片描述

  1. 创建事件标志组
    FreeRTOS 提供了两种创建事件标志组的方式,分别为动态方式创建事件标志组和静态方
    式创建事件标志组,两者的区别在于静态方式创建事件标志组时,需要用户提供创建事件标志
    组所需的内存空间,而使用动态方式创建事件标志组时,FreeRTOS 会自动从FreeRTOS 管理的
    堆中分配创建事件标志组所需的内存空间。
    动态方式创建事件标志组API 函数的函数原型如下所示:
EventGroupHandle_t xEventGroupCreate(void);

函数xEventGroupCreate()的形参描述,如下表所示:
在这里插入图片描述
静态方式创建事件标志组API 函数的函数原型如下所示:

EventGroupHandle_t xEventGroupCreateStatic(
StaticEventGroup_t * pxEventGroupBuffer);

函数xEventGroupCreateStatic()的形参描述,如下表所示:
在这里插入图片描述
2. 删除事件标志组
FreeRTOS 提供了用于删除事件标志组的API 函数,函数原型如下所示:

void vEventGroupDelete(EventGroupHandle_t xEventGroup);

在这里插入图片描述
3. 等待事件标志位
等待事件标志位使用的是函数xEventGroupWaitBits(),其函数原型如下所示:

EventBits_t xEventGroupWaitBits(
EventGroupHandle_t xEventGroup,
const EventBits_t uxBitsToWaitFor,
const BaseType_t xClearOnExit,
const BaseType_t xWaitForAllBits,
TickType_t xTicksToWait)

在这里插入图片描述
4. 设置事件标志位
FreeRTOS 提供了两个用于设置事件标志位的API 函数,这个两个函数分别用于在任务和
在中断中设置事件标志位。
在任务中设置事件标志位API 函数的函数原型如下所示:

EventBits_t xEventGroupSetBits(
EventGroupHandle_t xEventGroup,
const EventBits_t uxBitsToSet)

在这里插入图片描述
在中断中设置事件标志位API 函数的函数原型如下所示:

BaseType_t xEventGroupSetBitsFromISR(
EventGroupHandle_t xEventGroup,
const EventBits_t uxBitsToSet,
BaseType_t * pxHigherPriorityTaskWoken)

函数xEventGroupSetBitsFromISR()的形参描述,如下表所示:
在这里插入图片描述
5. 清零事件标志位
FreeRTOS 提供了两个用于清零事件标志位的API 函数,这个两个函数分别用于在任务和
在中断中清零事件标志位。
在任务中清零事件标志位API 函数的函数原型如下所示:

EventBits_t xEventGroupClearBits(
EventGroupHandle_t xEventGroup,
const EventBits_t uxBitsToClear)

函数xEventGroupClearBits()的形参描述,如下表所示:
在这里插入图片描述
函数xEventGroupClearBits()的返回值,如下表所示:
在这里插入图片描述
在中断中清零事件标志位API 函数的函数原型如下所示:

BaseType_t xEventGroupClearBitsFromISR(
EventGroupHandle_t EventGroup,
const EventBits_t uxBitsToClear)

在这里插入图片描述
6. 获取事件组中事件标志位的值
FreeRTOS 提供了两个用于获取事件组中事件标志位值的API 函数,这个两个函数分别用
于在任务和在中断中获取事件组中事件标志位的值。
在任务中获取事件组中事件标志位值API 函数的函数原型如下所示:

EventBits_t xEventGroupGetBits(xEventGroup);

在这里插入图片描述
7. 函数xEventGroupSync()
此函数一般用于多任务同步,其中每个任务都必须等待其他任务达到同步点,然后才能继
续执行。函数xEventGroupSync()的函数原型如下所示:

EventBits_t xEventGroupSync(
EventGroupHandle_t xEventGroup,
const EventBits_t uxBitsToSet,
const EventBits_t uxBitsToWaitFor,
TickType_t xTicksToWait)

在这里插入图片描述

FreeRTOS 事件标志组实验

功能设计

  1. 例程功能
    本实验主要用于学习FreeRTOS 事件标志组相关API 函数的使用,本实验设计了四个任务,
    这四个任务的功能如下表所示:
    在这里插入图片描述
    该实验的实验工程,请参考《FreeRTOS 实验例程16 FreeRTOS 事件标志组实验》。

软件设计

  1. 程序流程图
    本实验的程序流程图,如下图所示:
    在这里插入图片描述
  2. 程序解析
    整体的代码结构,请参考2.1.6 小节,本小节着重讲解本实验相关的部分。
    (1) start_task 任务
    start_task 任务的入口函数代码如下所示:
/**
 * @brief start_task
 * @param pvParameters : 传入参数(未用到)
 * @retval 无
 */
void start_task(void * pvParameters) {
    taskENTER_CRITICAL(); /* 进入临界区*/
    /* 创建事件标志组*/
    EventGroupHandler = xEventGroupCreate();
    /* 创建任务1 */
    xTaskCreate((TaskFunction_t) task1, (const char * )
        "task1", (uint16_t) TASK1_STK_SIZE, (void * ) NULL, (UBaseType_t) TASK1_PRIO, (TaskHandle_t * ) & Task1Task_Handler);
    /* 创建任务2 */
    xTaskCreate((TaskFunction_t) task2, (const char * )
        "task2", (uint16_t) TASK2_STK_SIZE, (void * ) NULL, (UBaseType_t) TASK2_PRIO, (TaskHandle_t * ) & Task2Task_Handler);
    /* 创建任务3 */
    xTaskCreate((TaskFunction_t) task3, (const char * )
        "task3", (uint16_t) TASK3_STK_SIZE, (void * ) NULL, (UBaseType_t) TASK3_PRIO, (TaskHandle_t * ) & Task3Task_Handler);
    vTaskDelete(StartTask_Handler); /* 删除开始任务*/
    taskEXIT_CRITICAL(); /* 退出临界区*/
}

start_task 任务主要用于创建事件标志组、task1 任务、task2 任务和task3 任务。
(2) task1 任务

/**
 * @brief task1
 * @param pvParameters : 传入参数(未用到)
 * @retval 无
 */
void task1(void * pvParameters) {
    uint8_t key = 0;
    while (1) {
        key = key_scan(0);
        switch (key) {
            case KEY0_PRES:
                {
                    /* 设置事件组的事件标志0 */
                    xEventGroupSetBits((EventGroupHandle_t) EventGroupHandler, (EventBits_t) EVENTBIT_0);
                    break;
                }
            case KEY1_PRES:
                {
                    /* 设置事件组的事件标志1 */
                    xEventGroupSetBits((EventGroupHandle_t) EventGroupHandler, (EventBits_t) EVENTBIT_1);
                    break;
                }
            default:
                {
                    break;
                }
        }
        vTaskDelay(10);
    }
}

task1 任务主要用于扫描按键,当按下按键0 时,设置事件组的事件标志0,当按下按键1
时,设置事件组的事件标志1。
(3) task2 任务

/**
 * @brief task2
 * @param pvParameters : 传入参数(未用到)
 * @retval 无
 */
void task2(void * pvParameters) {
    uint32_t task2_num = 0;
    while (1) {
        /* 等待事件标志0和事件标志1
         * 如果成功等待到了事件标志0和1,则清零事件标志0和1
         * 等待的方式为逻辑与,即事件标志0和1需要被同时设置
         */
        xEventGroupWaitBits((EventGroupHandle_t) EventGroupHandler, (EventBits_t) EVENTBIT_ALL, (BaseType_t) pdTRUE, (BaseType_t) pdTRUE, (TickType_t) portMAX_DELAY);
        /* LCD区域刷新*/
        lcd_fill(6, 131, 233, 313, lcd_discolor[++task2_num % 11]);
        vTaskDelay(10);
    }
}

可以看到task2 任务等待事件组中的事件标志0 和1 同时被设置,只有事件组中的视角标
志0 和1 被同时设置,task2 任务才会继续执行,同时如果成功等待到了事件标志0 和1 被同时
设置,那么还会自动清零事件组中的事件标志0 和1,无需手动清零。
(4) task3 任务

/**
 * @brief task3
 * @param pvParameters : 传入参数(未用到)
 * @retval 无
 */
void task3(void * pvParameters) {
    EventBits_t event_val = 0;
    while (1) {
        /* 获取事件组的所有事件标志值*/
        event_val = xEventGroupGetBits((EventGroupHandle_t) EventGroupHandler);
        /* 在LCD上显示事件值*/
        lcd_show_xnum(182, 110, event_val, 1, 16, 0, BLUE);
        vTaskDelay(10);
    }
}

从上面的代码可以看出,task3 任务获取了事件组的事件标志值,并将事件组的事件标志值
实时显示到LCD 屏幕上。

下载验证

编译并下载代码,复位后可以看到LCD 屏幕上显示了本次实验的相关信息,如下图所示:
在这里插入图片描述
一开始可以看到,事件组的事件标志值为0。此时按键按键0,来设置事件组的事件标志0,
可以看到LCD 上显示的事件组事件标志值为1,此值正是事件标志0 被设置后的值,如下图所
示:
图16.3.3.2 LCD 显示内容二

接着多次按下按键0,LCD 显示的内容都不会发生变化,因为事件标志0 已经被设置了。
接着按键按键1,来设置事件组的事件标志1,可以看到LCD 上显示的事件组事件标志值
直接清零,并且LCD 屏幕区域也刷新了,如下图所示:
在这里插入图片描述
这是因为,当事件标志1 被设置后,task2 任务立马就等待到了事件标志0 和1 同时被设
置,因此task2 任务就将事件组的事件标志0 和1 清零了,并且task2 解除阻塞得以运行,所以
LCD 屏幕区域被刷新了。
在本次实验中,不论按键0 或者按键1 中的哪一个按键先被按下,即不论事件组中的事件
标志0 还是事件标志1 中的哪一个先被设置,只要事件标志0 和事件标志1 存在同时处于被设
置的状态,那么task2 任务就等待事件组中的事件标志成功,于是task2 任务解除阻塞,得以执
行。

FreeRTOS 任务通知

FreeRTOS 内核V8.2.0 版本发布的时候,FreeRTOS 新增了任务通知这个功能,任务通知也
是用于任务间进行同步和通讯的一种机制,但是相对于前面章节介绍的队列、事件标志组和信
号量等而言,任务通知在内存占用和效率方面都有很大的优势。本章就来学习FreeRTOS 中任
务通知的相关内容。

FreeRTOS 任务通知简介

在FreeRTOS 中,每一个任务都有两个用于任务通知功能的数组,分别为任务通知数组和
任务通知状态数组。其中任务通知数组中的每一个元素都是一个32 位无符号类型的通知值;而
任务通知状态数组中的元素则表示与之对应的任务通知的状态。
任务通知数组中的32 位无符号通知值,用于任务到任务或中断到任务发送通知的“媒介”。
当通知值为0 时,表示没有任务通知;当通知值不为0 时,表示有任务通知,并且通知值就是
通知的内容。
任务通知状态数组中的元素,用于标记任务通知数组中通知的状态,任务通知有三种状态,
分别为未等待通知状态、等待通知状态和等待接收通知状态。其中未等待通知状态为任务通知
的复位状态;当任务在没有通知的时候接收通知时,在任务阻塞等待任务通知的这段时间内,
任务所等待的任务通知就处于等待通知状态;当有其他任务向任务发送通知,但任务还未接收
这一通知的这段期间内,任务通知就处于等待接收通知状态。
任务通知功能所使用到的任务通知数组和任务通知状态数组为任务控制块中的成员变量,
因此任务通知的传输是直接传出到任务中的,不同通过任务的通讯对象(队列、事件标志组和
信号量就属于通讯对象)这个间接的方式。间接通讯示意图如下所示:
在这里插入图片描述
任务通知则是直接地往任务中发送通知,直接通讯示意图如下所示:
在这里插入图片描述

任务通知的优势

使用任务通知向任务发送事件或数据比使用队列、事件标志组或信号量快得多;并且使用
任务通知代替队列、事件标志组或信号量,可以节省大量的内存,这是因为每个通讯对象在使
用之前都需要被创建,而任务通知功能中的每个通知只需要在每个任务中占用固定的5 字节内
存。

任务通知的缺点

虽然任务通知功能相比通讯对象,有着更快、占用内存少的优点,但是任务通知功能并不
能适用于所有情况,例如以下列出的几种情况:

  1. 发送事件或数据到中断
    通讯对象可以发送事件或数据从中断到任务,或从任务到中断,但是由于任务通知依赖于
    任务控制块中的两个成员变量,并且中断不是任务,因此任务通知功能并不适用于从任务往中
    断发送事件或数据的这种情况,但是任务通知功能可以在任务之间或从中断到任务发送事件或
    数据。
  2. 存在多个接收任务
    通讯对象可以被已知通讯对象句柄的任意多个任务或中断访问(发送或接收),但任务通知
    是直接发送事件或数据到指定接收任务的,因传输的事件或数据只能由接收任务处理。然而在
    实际中很少受到这种情况的限制,因为,虽然多个任务和中断发送事件或数据到一个通讯对象
    是很常见的,但很少出现多个任务或中断接收同一个通讯对象的情况。
  3. 缓冲多个数据项
    通讯对象中的队列是可以一次性保存多个已经被发送到队列,但还未被接收的事件或数据
    的,也就是说,通讯对象有着一定的缓冲多个数据的能力,但是任务通知是通过更新任务通知
    值来发送事件或数据的,一个任务通知值只能保存一次。
  4. 广播到多个任务
    通讯对象中的事件标志组是可以将一个事件同时发送到多个任务中的,但任务通知只能是
    被指定的一个接收任务接收并处理。
  5. 阻塞等待接收任务
    当通讯对象处于暂时无法写入的状态(例如队列已满,此时无法再向队列写入消息)时,
    发送任务是可以选择阻塞等待接收任务接收,但是任务因尝试发送任务通知到已有任务通知但
    还未处理的任务而进行阻塞等待的。但是任务通知也很少在实际情况中收到这种情况的限制。

FreeRTOS 任务通知相关API 函数

FreeRTOS 提供了任务通知的一些相关操作函数,其中任务通知相关API 函数,如下两表所示:

在这里插入图片描述
在这里插入图片描述
以上两表列出了FreeRTOS 提供的几个任务通知相关的操作函数,从第17.1 小节《FreeRTOS
任务通知简介》中,可以知道任务的任务控制块中,与任务通知功能相关的两个成员变量,任
务通知值和任务通知状态,是两个数组,也就是说,一个任务可以有多个任务通知,多个通知
就通过数组的下标进行索引。
表17.2.1 所列出的API 函数都是对任务通知相关数组中下标为0 的元素进行操作,而表
17.2.2 中列出的API 函数可以指定对任务通知相关数组中的元素进行操作。两表中对应的API
函数原理上是一样的,只是表17.2.1 中的API 是固定对任务的任务通知0 进行操作,而表17.2.2
中的API 函数可以对任务的指定任务通知进行操作,本文以表17.2.1 中的函数为例进行讲解。

  1. 发送任务通知
    表17.2.1 中发送任务通知的三个API 函数的定义如下所示:
#
define xTaskNotify(xTaskToNotify, \
    ulValue, \
    eAction)\
xTaskGenericNotify((xTaskToNotify), \ (tskDEFAULT_INDEX_TO_NOTIFY), \ (ulValue), \ (eAction), \
    NULL)# define xTaskNotifyAndQuery(xTaskToNotify, \
    ulValue, \
    eAction, \
    pulPreviousNotifyValue)\
xTaskGenericNotify((xTaskToNotify), \ (tskDEFAULT_INDEX_TO_NOTIFY), \ (ulValue), \ (eAction), \ (pulPreviousNotifyValue))# define xTaskNotifyGive(xTaskToNotify)\
xTaskGenericNotify((xTaskToNotify), \ (tskDEFAULT_INDEX_TO_NOTIFY), \ (0), \
    eIncrement, \
    NULL)

从上面的代码中可以看出,三个用于在任务中发送任务通知的函数,实际上都是调用了函
数xTaskGenericNotify()来发送任务通知的,只是传入了不同的参数。函数xTaskGenericNotify()
的函数原型如下所示:

BaseType_t xTaskGenericNotify(TaskHandle_t xTaskToNotify,
    UBaseType_t uxIndexToNotify,
    uint32_t ulValue,
    eNotifyAction eAction,
    uint32_t * pulPreviousNotificationValue);

函数xTaskGenericNotify()的形参描述,如下表所示:
在这里插入图片描述
函数xTaskGenericNotify()的返回值,如下表所示:
在这里插入图片描述
函数xTaskGenericNotify()的源代码如下所示:
。。。。

结合函数xTaskNotify()、函数xTaskNotifyAndQuery()、函数xTaskNotifyGive()的定义和以
上代码,可以知道函数xTaskNotify()、函数xTaskNotifyAndQuery()、函数xTaskNotifyGive()的
作用如下所示:
函数xTaskNotify():
此函数用于往指定任务发送任务通知,通知方式可以自由指定,并且不获取发送任务通知
前任务通知的通知值。
函数xTaskNotifyAndQuery():
此函数用于往指定任务发送任务通知,通知方式可以自由指定,并且获取发送任务通知前
任务通知的通知值。
函数xTaskNotifyGive():
此函数用于往指定任务发送任务通知,通知方式为将通知值加1,并且不获取发送任务通
知前任务通知的通知值。
2. 在中断中发送任务通知
表17.2.1 中在中断中发送任务通知的三个API 函数的定义如下所示:

#define xTaskNotifyFromISR( xTaskToNotify, \
ulValue, \
eAction, \
pxHigherPriorityTaskWoken) \
xTaskGenericNotifyFromISR( (xTaskToNotify), \
(tskDEFAULT_INDEX_TO_NOTIFY), \
(ulValue), \
(eAction), \
NULL, \
(pxHigherPriorityTaskWoken))
#define xTaskNotifyAndQueryFromISR( xTaskToNotify, \
ulValue, \
eAction, \
pulPreviousNotificationValue, \
pxHigherPriorityTaskWoken) \
xTaskGenericNotifyFromISR( (xTaskToNotify), \
(tskDEFAULT_INDEX_TO_NOTIFY), \
(ulValue), \
(eAction), \
(pulPreviousNotificationValue), \
(pxHigherPriorityTaskWoken))
#define vTaskNotifyGiveFromISR( xTaskToNotify, \
pxHigherPriorityTaskWoken ) \
vTaskGenericNotifyGiveFromISR( (xTaskToNotify), \
(tskDEFAULT_INDEX_TO_NOTIFY), \
( pxHigherPriorityTaskWoken));

从上面的代码可以看出,函数xTaskNotifyFromISR()和函数xTaskNotifyAndQueryFromISR()
实际上都是调用了函数xTaskGenericNotifyFromISR(),而函数vTaskNotifyGiveFromISR()实际上
则是调用了函数vTaskGenericNotifyGiveFromISR()。下面就分别看一下以上这两个实际被调用
的函数。
函数xTaskGenericNotifyFromISR()的函数原型如下所示:

BaseType_t xTaskGenericNotifyFromISR(
TaskHandle_t xTaskToNotify,
UBaseType_t uxIndexToNotify,
uint32_t ulValue,
eNotifyAction eAction,
uint32_t * pulPreviousNotificationValue,
BaseType_t * pxHigherPriorityTaskWoken);

在这里插入图片描述
函数xTaskGenericNotifyFromISR()的源代码如下所示:
。。。。

从上面的代码中可以看出,函数xTaskGenericNotifyFromISR()于函数xTaskNotify()是很相
似的,只是多了对中断做了一些相应的处理。
函数vTaskGenericNotifyGiveFromISR()的函数原型如下所示:

void vTaskGenericNotifyGiveFromISR(
TaskHandle_t xTaskToNotify,
UBaseType_t uxIndexToNotify,
BaseType_t * pxHigherPriorityTaskWoken);

在这里插入图片描述
函数vTaskGenericNotifyGiveFromISR()的源代码如下所示:
。。。。

从以上代码中可以看出,函数vTaskGenericNotifyGiveFromISR()就是通知方式为eIncrement
并且没有返回值的函数xTaskGenericNotifyFromISR()。
结合以上函数xTaskGenericNotifyFromISR()和函数vTaskGenericNotifyGiveFromISR()的源
代码和函数xTaskNotifyFromISR() 和函数xTaskNotifyAndQueryFromISR() 和函数
vTaskNotifyGiveFromISR()的定义,表17.2.1 中列出的三个在中断中发送任务通知的API 函数作
用如下:
函数xTaskNotifyFromISR()
此函数用于在中断中往指定任务发送任务通知,通知方式可以自由指定,并且不获取发送
任务通知前任务通知的通知值,但获取发送通知后是否需要进行任务切换的标志。
函数xTaskNotifyAndQueryFromISR()
此函数用于在中断中往指定任务发送任务通知,通知方式可以自由指定,并且获取发送任
务通知前任务通知的通知值,和发送通知后是否需要进行任务切换的标志。
函数vTaskNotifyGiveFromISR()
此函数用于在中断中往指定任务发送任务通知,通知方式为将通知值加1,并且不获取发
送任务通知前任务通知的通知值,但获取发送通知后是否需要进行任务切换的标志。
2. 接收任务通知
用于获取任务通知的API 函数有两个,分别为函数ulTaskNotifyTake() 和函数
xTaskNotifyWait()。
函数ulTaskNotifyTake()
此函数用于获取任务通知的通知值,并且在成功获取任务通知的通知值后,可以指定将通知
值清零或减1。此函数实际上是一个宏定义,具体的代码如下所示:

#define ulTaskNotifyTake(xClearCountOnExit, \
    xTicksToWait)\
ulTaskGenericNotifyTake((tskDEFAULT_INDEX_TO_NOTIFY), \ (xClearCountOnExit), \ (xTicksToWait))

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

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

相关文章

【Leetcode】241. 为运算表达式设计优先级

241. 为运算表达式设计优先级(中等) 解法一:分治法 对于这道题,加括号其实就是决定运算次序,所以我们可以把加括号转化为,「对于每个运算符号,先执行处理两侧的数学表达式,再处理此…

提高APP安全性的必备加固手段——深度解析代码混淆技术

APP 加固方式 Android APP 加固是优化 APK 安全性的一种方法,常见的加固方式有混淆代码、加壳、数据加密、动态加载等。下面介绍一下 Android APP 加固的具体实现方式。 混淆代码: 使用 ProGuard 工具可以对代码进行混淆,使得反编译出来的代…

makefile语法解析

gcc语法简介 gcc (-c) hello.c world.c -o main-c表示只编译不链接 -o目标文件 此外,还有-I(大写的I)表示给gcc添加自定义的头文件的路径 -L表示给gcc添加额外的搜索库的路径 -g选项的意义是“生成调试信息,该程序可以被调试器调…

【Java入门合集】第五章抽象类和接口(二)

博主:命运之光 专栏:JAVA入门 学习目标 1.了解什么是抽象类,什么是接口; 2.掌握抽象类和接口的定义方法; 3.理解接口和抽象类的使用场景; 4.掌握多态的含义和用法; 5.掌握内部类的定义方法和使用…

【Hive实战】Hive元数据存储库数据增多的分析

Hive元数据存储库数据增多的分析 2023年5月8日 文章目录 Hive元数据存储库数据增多的分析问题新增Hive相关的DDL操作创建Hive库库授权到用户 创建Hive表 内部表非分区表表授权到用户一级分区表二级分区表分桶表分桶排序表 查询指令核心表分析表关系图表以库表为主以hive表为主以…

人人都可用的ChatGPT,Edge浏览器-免费ChatGPT保姆级教程!非常详细!

人工智能大浪潮已经来临,对于ChatGPT,我觉得任何一个玩互联网的人,都应该重视起来,用起来。但是国内使用需要解决科学上网、注册、收费等繁琐问题。 所以,今天这篇文章就来推荐一个插件,无需任何繁琐操作&…

第一次省赛团队训练 - BAPC 2022 Pre(DAPC 2022)

B (2). Bubble-bubble Sort [ 提交记录 ] [ 问题 3462 ] 时间限制: 2000MS 空间限制: 256MB 结果评判: 文本对比 正确/提交: 7 (5) / 15 官方标签: 用户标记: 题目描述 Bubbles! As a fanatical supporter of the Bubbles Are Perfect Creatures movement, you have ac…

第8章 未执行缓存的强制清理操作导致显示异常解决方案

1 异常产生原因&#xff1a; 由于未为Role实体定义相就的缓存强制销毁器类&#xff1a;Services.Customers.Caching.RoleCacheEventConsumer,从而导致Services.Events.EventPublisher.PublishAsync<TEvent>(TEvent event)中的 consumers实例为0,如下图所示&#xff1a; 2…

深入理解移动端布局:Viewport与设备像素比

在移动端开发中&#xff0c;了解和掌握不同设备的布局特点是非常重要的。本文将介绍两个关键概念&#xff1a;Viewport 和设备像素比&#xff08;DPR&#xff09;&#xff0c;帮助你更好地理解移动端布局。 一、什么是 Viewport&#xff1f; Viewport 是用户在浏览器中可见的网…

JS知识点(包括原型,原型对象,数据类型,数据类型的检测)

目录 1、JavaScript有哪些数据类型&#xff0c;它们的区别&#xff1f; 2、基本数据类型和引用数据类型地区别&#xff1a; 3、数据类型检测的方式有哪些: 4、判断数组的方式有那些&#xff1f; 5、null和undefined区别&#xff1a; 6、为什么typeOf null得到object而不是n…

22个提升生产力的工具推荐,稳了

子曰&#xff1a;工欲善其事&#xff0c;必先利其器。 本文给大家推荐22个提高生产力的工具&#xff0c;总有一款符合你的需求。&#x1f604;&#x1f604;&#x1f604; 提高生产效率工具推荐 滴答清单/Todoist文件检索利器&#xff1a;Everything文件管理软件-Allen Explor…

基于SpringBoot的大学生租房系统

背景 大学生租房系统设计的目的是建立一个高效的平台&#xff0c;采用简洁高效的Java语言与Mysql数据库等技术&#xff0c;设计和开发了本大学生租房系统设计。该系统主要实现了用户和房主通过系统注册用户&#xff0c;登录系统后能够编辑自己的个人信息、查看首页&#xff0c…

【电子学会】2023年03月图形化三级 -- 猫猫的儿童节

猫猫的儿童节 儿童节到了&#xff0c;给小猫绘制一个七彩的气球。 1. 准备工作 &#xff08;1&#xff09;保留小猫角色&#xff1b; &#xff08;2&#xff09;选择“Button2”角色&#xff0c;添加文字“开始”&#xff1b; &#xff08;3&#xff09;默认白色背景。 2…

有人抱怨Android找不到工作,有人却收到了好几个Offer~

不知不觉&#xff0c;往年常说的面试黄金季就这样过去了&#xff0c;相信现在很多人都会抱怨说&#xff0c;现在是市场岗位缩水裁员季。有人抱怨&#xff0c;自然也有人喜悦&#xff0c;有失业人群在&#xff0c;自然就业人群也有&#xff0c;有人想找一份合理工作很难&#xf…

C高级(day1)

作业: 初始工作路径不在家目录下&#xff0c;在不切换路径的情况下&#xff0c;在家目录下创建一个subdir目录&#xff0c;在subdir这个目录下&#xff0c;创建subdir1和subdir2&#xff0c;并且把/etc/passwd拷贝到subdir1中&#xff0c;把/etc/group文件拷贝到subdir2中&…

David Silver Lecture 5: Model-Free Control

1 Introduction 1.1 内容 上一章是对一个unknown MDP进行value function的预测&#xff0c;相当于policy evaluation。这一章是对unknown MDP找到一个最优的policy&#xff0c; optimise value function. 1.2 On and Off-Policy Learning On-policy learning learn on the…

[oeasy]python0050_动态类型_静态类型_编译_运行

动态类型_静态类型 回忆上次内容 上次了解了 帮助文档的 生成 开头的三引号注释 可以生成 帮助文档文档 可以写成网页 python3 本身 也有 在线的帮助手册 目前的程序 提高了 可读性 有什么方法 可以让程序 更可读么&#xff1f;&#x1f914; 变量名 首先 在变量名上想办…

opencv_c++学习(六)

一、视频加载与摄像头调用 视频、摄像头加载 VideoCapture(filename, CAP_ANY)对以上实例解释如下&#xff1a; 若读取的为本地视频&#xff0c;则filename为视频名称&#xff0c;若读取的是摄像头数据&#xff0c;则为int类型的摄像头id。 视频属性的获取 视频属性可以通过…

手握美团offer,结果背调红灯,哭了....

相信很多人都会包装简历&#xff0c;尤其是工作经历&#xff0c;不过也有人会填一下虚假的背景信息&#xff0c;比如公司leader或HR&#xff0c;小公司没有实力过多进行背调&#xff0c;但是大企业就不同了&#xff0c;他们有方法了解到实际的情况。 背调包括候选人以往的经历…

RHCSA之Linux的安装步骤

目录 RHCSA之环境配置 需要的软件 VMwareWorkstation安装 1.打开VMwareWorkstation安装包 2.进入安装界面点击下一步 3. 在我接受许可协议打 √ 后&#xff0c;点击下一步 4.在安装位置选择更改 5. 更改目标安装位置&#xff0c;点击确定 6.疯狂点击下一步 8.点击安装 9.…