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 已经超时、即使它的优先级更高,都没办法执行。

 

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

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

相关文章

保护您的数据库免受注入攻击:MSSQL注入入门指南

MSSQL注入的入门讲解 一、引言二、MSSQL注入的基础知识2.1、MSSQL数据库的基本原理和结构2.2、常见的SQL语句和操作2.3、MSSQL注入的原理和工作方式 三、MSSQL注入攻击技术3.1、基于错误的注入攻击&#xff1a;利用错误消息和异常信息3.2、基于时间的注入攻击&#xff1a;利用延…

Nacos 架构原理

基本架构及概念​ 服务 (Service)​ 服务是指一个或一组软件功能&#xff08;例如特定信息的检索或一组操作的执行&#xff09;&#xff0c;其目的是不同的客户端可以为不同的目的重用&#xff08;例如通过跨进程的网络调用&#xff09;。Nacos 支持主流的服务生态&#xff0c…

Vue3 的 inject 和 provide (附源码)

一&#xff1a;前言 在前端项目中牵扯的最多的莫过于组件之间的传值了&#xff0c;除了最最常用的 props 和 emit&#xff0c;其实在 Vue 中还额外提供了另外几种方法。今天分享一种组件之间通信的方法&#xff1a;provide 和 inject。 二&#xff1a;使用 1、目录结构 以下是…

配置攻击防范示例

1、组网需求。 如果局域网内存在Hacker向SwitchA发起畸形报文攻击、分片报文攻击和泛洪攻击&#xff0c;将会造成SwitchA瘫痪。为了预防这种情况&#xff0c;管理员希望通过在SwitchA上部署各种攻击防范措施来为用户提供安全的网络环境&#xff0c;保障正常的网络服务。 2、配…

【测试开发】第五节.测试——自动化测试(Selenium工具)

作者简介&#xff1a;大家好&#xff0c;我是未央&#xff1b; 博客首页&#xff1a;未央.303 系列专栏&#xff1a;Java测试开发 每日一句&#xff1a;人的一生&#xff0c;可以有所作为的时机只有一次&#xff0c;那就是现在&#xff01;&#xff01;&#xff01; 前言 一、…

匿名结构体类型、结构体的自引用、结构体的内存对齐以及结构体传参

文章目录 &#x1f680;前言&#x1f680;结构体✈️结构体类型的声明✈️结构体变量的创建与初始化✈️结构体类型的特殊声明✈️结构体的自引用✈️结构体的内存对齐&#x1f681;修改默认对齐数 ✈️结构体传参 &#x1f680;前言 在C语言中有着各种数据类型&#xff0c;这…

Linux部署HDFS集群

&#xff08;一&#xff09;VMware虚拟机中部署 ps、其中node1、node2、node3替换为自己相应节点的IP地址&#xff0c;或者host文件中配置过的主机名&#xff0c;或者看前置准备 或者查看前置准备&#xff1a;Linux部署HDFS集群前置准备 1.下载压缩包 https://www.apache.or…

ChatGPT 问世一周年之际,开源大模型能否迎头赶上?

就在11月30日&#xff0c;ChatGPT 迎来了它的问世一周年&#xff0c;这个来自 OpenAI 的强大AI在过去一年里取得了巨大的发展&#xff0c;迅速吸引各个领域的用户群体。 我们首先回忆一下 OpenAI和ChatGPT这一年的大事记&#xff08;表格由ChatGPT辅助生成&#xff09;&#x…

【模型报错记录】‘PromptForGeneration‘ object has no attribute ‘can_generate‘

通过这个连接中的方法解决&#xff1a; “PromptForGeneration”对象没有属性“can_generate” 期刊 #277 thunlp/OpenPrompt GitHub的 问题描述&#xff1a;在使用model.generate() 的时候报错&#xff1a;PromptForGeneration object has no attribute can_generate 解决方法…

MySQL安全相关——TDE和数据脱敏功能介绍

MySQL作为一款广泛使用的开源关系型数据库管理系统(RDBMS)&#xff0c;其安全性一直是开发者和企业关注的重点。在MySQL中&#xff0c;有一些与安全相关的功能&#xff0c;其中包括Transparent Data Encryption(TDE)和数据脱敏。本文将对这些功能进行介绍。 一、Transparent Da…

Python一键采集京*东商品数据,保存表格,零基础也能学会

嗨喽~大家好呀&#xff0c;这里是魔王呐 ❤ ~! python更多源码/资料/解答/教程等 点击此处跳转文末名片免费获取 开发环境: python 3.8 pycharm 专业版 模块使用&#xff1a; requests >>> 发送请求 第三方库 (需要安装) parsel >>> 第三方库 用来提取网…

RHCSA学习笔记(RHEL8) - Part1.RH124

Chapter Ⅰ 入门 - Linux 开源系统&#xff0c;命令行&#xff0c;模块化&#xff08;软件包的形势&#xff09; - Windows 闭源Linux是类UNIX系统&#xff0c;mac系统也是类UNIX系统&#xff0c;所以二者的图形化界面比较相似 开源许可证&#xff1a;公共版权&#xff1b;宽…

学校图书管理系统设计

基于ASP.NET MVC技术的图书管理系统的设计与实现 摘要&#xff1a;图书管理系统是一套高新科学技术和图书知识信息以及传统历史文化完美结合的体现。它改变了传统图书收藏的静态书本式图书服务特征&#xff0c;实现了多媒体存取、远程网络传输、智能化检索、跨库无缝链接、创造…

《地理信息系统原理》笔记/期末复习资料(8. 数字高程模型)

目录 8. 数字高程模型 8.1 概述 8.1.1 数字高程模型概念 8.1.2 数字高程模型特点 8.2 DEM数据分布特征 8.2.1 格网状数据 8.2.2 离散数据 8.3 DEM的表示方法 8.3.1 数学方法 8.3.2 图形方法 8.3.3 DEM三维表达方法 8.4 TIN的生成方法 8.4.1 人工方法 8.4.2 程序自…

selenium 工具 的基本使用

公司每天要做工作汇报&#xff0c;汇报使用的网页版&#xff0c; 所以又想起 selenium 这个老朋友了。 再次上手&#xff0c;发现很多接口都变了&#xff0c; 怎么说呢&#xff0c; 应该是易用性更强了&#xff0c; 不过还是得重新看看&#xff0c; 我这里是python3。 pip安装…

数字员工「取数宝」上新!4大优势,解决电商取数难题

全域电商&#xff0c;是近几年的新趋势&#xff0c;几乎所有商家都在布局全域&#xff0c;追求全域增长。但商家发现&#xff0c;随着投入成本的上涨&#xff0c;利润却没有增加。 其中最为突出的是——商家为保证全域数据的及时更新&#xff0c;通过堆人头的方式完成每日取数…

带你用uniapp从零开发一个仿小米商场_10.开发一个占剩余窗口的滚动区域

首先是一个头部的tag切换栏,这个很简单,就不多说 源码奉上 <scroll-view scroll-x class"border scroll-row" style"height: 80rpx;"><view class"scroll-row-item" style"height: 80rpx;line-height: 80rpx;" v-for"(…

二分查找边界问题——排序数组找元素第一次出现和最后一次出现

二分查找的边界逼近问题&#xff1a; 下面的代码&#xff0c;第一个函数会向左边界逼近&#xff0c;第二个函数会像右边界逼近&#xff01; 考虑left5,right6这种情况&#xff0c;如果5&#xff0c;6的值都是满足的条件的怎么办&#xff1f; 如果mid(leftright1)/2&#xff0c;…

赤峰学院师资培养管理系统的设计与实现

摘 要 随着我国国民经济建设的蓬勃发展和信息技术的越发成熟&#xff0c;各个行业都在积极使用现代化的管理工具&#xff0c;不断改善企业的服务质量&#xff0c;提高工作效率。对师资培养进行现代化的管理&#xff0c;提高工作效率是师资培养管理系统的一大优点。本文是一篇关…

opencv学习二:加载显示图片

文章目录 加载显示图片&#xff08;一&#xff09;函数1.imread()读取图片&#xff08;1&#xff09;matplotlib和opencv中imread函数的区别 加载显示图片 &#xff08;一&#xff09;函数 1.imread()读取图片 Mat imread(const string& filename, int flags1 );第一个参…