跟着野火学FreeRTOS:第二段(事件组)

     在小节里面介绍了二进制信号量,计数信号量,互斥量和递归互斥量等功能,其中二进制信号量和计数信号量(也包括队列)常用于任务和任务之间以及任务和中断之间的同步,她们具有以下属性:

  • 当等待的事件没有发生的时候可以让等待的任务进入阻塞状态
  • 当等待的事件发生之后可以将因为等待事件发生而进入阻塞状态的任务唤醒,如果有多个任务在等待同一个事件发生的话,那么此时被唤醒的是优先级最高的任务,其它任务依然还是处于阻塞状态。

     这里即将要介绍的事件组( E v e n t G r o u p s Event\quad Groups EventGroups)也可以用于任务和任务之间以及任务和中断之间的同步,但是和二进制信号量,计数信号量(也包括队列)不同的是:

  1. 事件组可以用于等待多个事件的发生而进行同步,而不是像二进制信号量和计数信号量(也包括队列)只能用于等待单一的事件而进行同步
  2. 当等待的事件中的事件发生的时候可以唤醒多个任务而不是像二进制信号量和计数信号量(也包括队列)只能用于唤醒多个任务中优先级最高的那个任务。

     事件组以上的特有属性使得事件组非常适合于多个任务的同步,可以将事件的发生广播给多个任务,也允许一个任务等待一个或多个事件的发生来同步。事件组在 F r e e R T O S FreeRTOS FreeRTOS的源码的 e v e n t g r o u p s . c event_groups.c eventgroups.c文件中用一个结构体来定义,如图1所示。当宏 c o n f i g U S E _ 16 _ B I T _ T I C K S configUSE\_16\_BIT\_TICKS configUSE_16_BIT_TICKS被定义为0的时候,变量 u x E v e n t B i t s uxEventBits uxEventBits的低24位可以用来表示24个事件的状态,如果对应的比特位的值为1则表面对应的事件已经发生,反之对应的事件没有发生,变量 u x E v e n t B i t s uxEventBits uxEventBits的高8位用于特殊用途(对应于图2中的那些宏定义,至于其具体的含义需要通过相应的源码来了解其具体含义)。当宏 c o n f i g U S E _ 16 _ B I T _ T I C K S configUSE\_16\_BIT\_TICKS configUSE_16_BIT_TICKS被定义为1的时候,变量 u x E v e n t B i t s uxEventBits uxEventBits的低8位可以用来表示8个事件的状态,如果对应的比特位的值为1则表面对应的事件已经发生,反之对应的事件没有发生,变量 u x E v e n t B i t s uxEventBits uxEventBits的高8位用于特殊用途(对应于图2中的那些宏定义,至于其具体的含义需要通过相应的源码来了解其具体含义)。

图1.
图2.

     在图3的例子中,这里宏 c o n f i g U S E _ 16 _ B I T _ T I C K S configUSE\_16\_BIT\_TICKS configUSE_16_BIT_TICKS被定义为0,比特位1,4,和7对应的事件已经发生而其它比特位对应的事件还没有发生。事件组可以被多个任务或中断接入,任何任务都有权限对事件组结构体中的变量 u x E v e n t B i t s uxEventBits uxEventBits的低8位或低24位的比特位进行置位,任何任务也都有权限对事件组结构体中的变量 u x E v e n t B i t s uxEventBits uxEventBits进行读取。事件组的结构体的 x T a s k s W a i t i n g F o r B i t s xTasksWaitingForBits xTasksWaitingForBits元素是一个用来记录因为在等待事件组的某个或某些事件而被阻塞的任务。

图3.

     下面来简单的看几个事件组的 A P I API API接口函数,其它的 A P I API API接口函数可以自己去看看图4所示的文档的详细介绍。

图4.
EventBits_t xEventGroupSetBits( EventGroupHandle_t xEventGroup, const EventBits_t uxBitsToSet )
    BaseType_t xEventGroupSetBitsFromISR( EventGroupHandle_t xEventGroup,const EventBits_t uxBitsToSet,BaseType_t * pxHigherPriorityTaskWoken ){BaseType_t xReturn;traceEVENT_GROUP_SET_BITS_FROM_ISR( xEventGroup, uxBitsToSet );xReturn = xTimerPendFunctionCallFromISR( vEventGroupSetBitsCallback, ( void * ) xEventGroup, ( uint32_t ) uxBitsToSet, pxHigherPriorityTaskWoken ); /*lint !e9087 Can't avoid cast to void* as a generic callback function not specific to this use case. Callback casts back to original type so safe. */return xReturn;}EventBits_t xEventGroupWaitBits( EventGroupHandle_t xEventGroup,const EventBits_t uxBitsToWaitFor,const BaseType_t xClearOnExit,const BaseType_t xWaitForAllBits,TickType_t xTicksToWait )

     接口 x E v e n t G r o u p S e t B i t s xEventGroupSetBits xEventGroupSetBits用来对事件组的结构体里面的变量 u x E v e n t B i t s uxEventBits uxEventBits的低24位或低8位的比特位进行置位,表示对应的比特位表示的事件已经发生,参数 x E v e n t G r o u p xEventGroup xEventGroup表示将要对其进行置位操作的事件组,参数 u x B i t s T o S e t uxBitsToSet uxBitsToSet表示将要对那些比特位进行置位,如果参数 u x B i t s T o S e t uxBitsToSet uxBitsToSet的值为 0 x 00000024 0x00000024 0x00000024且置位操作之前事件组的结构体里面的变量 u x E v e n t B i t s uxEventBits uxEventBits的值为 0 x 00008100 0x00008100 0x00008100,那置位之后事件组的结构体里面的变量 u x E v e n t B i t s uxEventBits uxEventBits的值应该为 0 x 00008124 0x00008124 0x00008124(假设在此期间没有其它任务对这个事件组进行置位或清除操作),也就是说置位操作不会将置位操作以前已经是值1的比特位变成值0,这是因为在进行置位操作的时候进行的是或运算。该接口不能在中断中调用,因为 F r e e R T O S FreeRTOS FreeRTOS的设计理念不允许在中断中进行会产生不确定行行为的操作,这是因为事件组中的事件的发生可以唤醒多个事件,至于具体是几个,我们是无法知道的,因此对事件组的置位操作属于是不确定的操作。该接口对应的中断版本为 x E v e n t G r o u p S e t B i t s F r o m I S R xEventGroupSetBitsFromISR xEventGroupSetBitsFromISR,该接口将事件组的置位操作(其实最后调用的也是接口 x E v e n t G r o u p S e t B i t s xEventGroupSetBits xEventGroupSetBits)转移到 t h e R T O S d a e m o n t a s k the\quad RTOS\quad daemon\quad task theRTOSdaemontask(该点涉及的内容应该是在 S o f t w a r e T i m e r M a n a g e m e n t Software\quad Timer\quad Management SoftwareTimerManagement章节)中进行。我们再来看以下接口 x E v e n t G r o u p W a i t B i t s xEventGroupWaitBits xEventGroupWaitBits,该接口用来等待事件组中的某个单一事件或多个事件的发生,如果等待的事件没有发生则等待参数 x T i c k s T o W a i t xTicksToWait xTicksToWait指定的时间,如果在这个参数指定的时间之内等待的事件发生了,则等待事件的任务会被马上唤醒,如果在这个参数指定的时间之内等待的事件没有发生,也就是等待超时了,等待事件的任务也会离开等待状态。参数 u x B i t s T o W a i t F o r uxBitsToWaitFor uxBitsToWaitFor表示要等待的事件,参数 x W a i t F o r A l l B i t s xWaitForAllBits xWaitForAllBits就很好的体现了事件组的使用可以用来等待多个事件的这个特殊属性,当这个参数的值为pdFALSE的时候,如果参数 u x B i t s T o W a i t F o r uxBitsToWaitFor uxBitsToWaitFor指定了5个事件,那么只要这5个事件中的任何一个发生,等待的任务就会被唤醒,当这个参数的值为pdTRUE的时候,如果参数 u x B i t s T o W a i t F o r uxBitsToWaitFor uxBitsToWaitFor指定了5个事件,那么只有这5个事件全部发生的时候,等待的任务才会被唤醒。
     接下来弄了个简单的例子来简单的跑一跑事件组,主要代码如下所示,完整的工程代码在这里,这个例子创建了两个任务,一个任务用来等待事件组中的三个事件的发生,如果这三个事件没有发生的话就一直等待,另一个任务用来对这三个事件在事件组中对应的比特位进行置位,这三个事件分别对应开发板上面的三个按键的一次按下,当开发板上面的三个按键都被按下一次之后,等待事件的任务就会被唤醒。

static EventGroupHandle_t Event_Handle =NULL;/* Definitions for the event bits in the event group. */
#define KEY0_PRESSED_BIT   ( 1UL << 0UL ) /* Event bit 0, which is set by a task. */
#define KEY1_PRESSED_BIT   ( 1UL << 5UL ) /* Event bit 5, which is set by a task. */
#define KEY_UP_PRESSED_BIT ( 1UL << 9UL ) /* Event bit 9, which is set by a task. */static void vEventBitSettingTask(void* parameter)
{	while (1){if( Key_Scan(1) == KEY0_PRES )       {printf( "KEY 0 has been pressed.\r\n" );					xEventGroupSetBits(Event_Handle,KEY0_PRESSED_BIT);  									}if( Key_Scan(1) == KEY1_PRES )       {printf( "KEY 1 has been pressed.\r\n" );					xEventGroupSetBits(Event_Handle,KEY1_PRESSED_BIT);  									}if( Key_Scan(1) == WKUP_PRES )       {printf( "KEY UP has been pressed.\r\n" );					xEventGroupSetBits(Event_Handle,KEY_UP_PRESSED_BIT);  									}				vTaskDelay(pdMS_TO_TICKS(20));  }	
}static void vEventBitReadingTask(void* parameter)
{	 BaseType_t xReturn = pdTRUE;while (1){xEventGroupWaitBits(Event_Handle, KEY0_PRESSED_BIT | KEY1_PRESSED_BIT | KEY_UP_PRESSED_BIT,pdTRUE,   pdTRUE,portMAX_DELAY);printf( "All three keys has been pressed.\r\n" );									}
}int main(void)
{	 NVIC_PriorityGroupConfig( NVIC_PriorityGroup_4);KEY_Init();uart_init(115200);	printf("Event groups demo start.\r\n");/* Before an event group can be used it must first be created. */Event_Handle = xEventGroupCreate();/* Create the task that sets event bits in the event group. */xTaskCreate( vEventBitSettingTask, "Bit Setter", 1000, NULL, 1, NULL );/* Create the task that waits for event bits to get set in the event group. */xTaskCreate( vEventBitReadingTask, "Bit Reader", 1000, NULL, 2, NULL );/* Start the scheduler so the created tasks start executing. */vTaskStartScheduler();	/* If all is well then main() will never reach here as the scheduler willnow be running the tasks. If main() does reach here then it is likely thatthere was insufficient heap memory available for the idle task to be created.Chapter 2 provides more information on heap memory management. */while(1);   	
}

     下面来看一下用接口 x E v e n t G r o u p S e t B i t s xEventGroupSetBits xEventGroupSetBits和接口 x E v e n t G r o u p W a i t B i t s xEventGroupWaitBits xEventGroupWaitBits不太好实现多个任务同步的场景,假设任务 A A A接收到某事件之后需要将和此事件相应的操作分担给任务 B B B,任务 C C C和任务 D D D去执行,在任务 B B B,任务 C C C和任务 D D D完成相应的操作之前,任务 A A A不会再接收事件,因此这4个任务需要相互同步。每一个任务的同步点都是对应的任务已经完成了相应的操作,任务 A A A的同步点就是接收到了事件,任务 B B B,任务 C C C和任务 D D D的同步点都是完成了和任务 A A A接收的事件对应的操作,每一个任务到达自己的同步点之后都不能再继续前进,除非任务 A A A,任务 B B B,任务 C C C和任务 D D D都到达了自己的同步点。此时这种场景可以用接口 x E v e n t G r o u p S y n c xEventGroupSync xEventGroupSync来实现多个任务的同步,这个接口的原型如下所示:

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

     参数 u x B i t s T o S e t uxBitsToSet uxBitsToSet用来对事件组的结构体里面的变量 u x E v e n t B i t s uxEventBits uxEventBits的低24位或低8位的比特位进行置位,对于上面的场景来说就相当于任务 A A A的接收到了事件,任务 B B B,任务 C C C或任务 D D D完成了和任务 A A A接收的事件对应的操作,参数 u x B i t s T o W a i t F o r uxBitsToWaitFor uxBitsToWaitFor用来表示要等待的事件,对于上面的场景来说就相当于任务 A A A,任务 B B B,任务 C C C和任务 D D D都到达了自己的同步点。接下来再弄了个简单的例子来简单的跑一跑接口 x E v e n t G r o u p S y n c xEventGroupSync xEventGroupSync,主要代码如下所示,这个例子模拟了上面的场景,创建了4个任务,其实哲理创建的4个任务的函数都是一样的,根据传入的参数来区别是那一个任务,每一个任务的延时不同,用来模拟所需相应操作的时间。任务 A A A延时1秒,任务 B B B延时2秒,任务 C C C延时5秒,任务 D D D延时10秒,任务 A A A延时最少是因为上面的场景是任务 A A A在接收到事件时候,任务 B B B,任务 C C C和任务 D D D才开始相关操作的执行,至于任务 B B B,任务 C C C和任务 D D D每个任务具体的相关操作的时间都是不固定的。我们从打印中可以看出只有当所有任务的延时都结束之后,任务 A A A,任务 B B B,任务 C C C和任务 D D D才会一同退出同步点。这里任务中在执行打印语句的时候会先进入临界段( C r i t i c a l S e c t i o n s Critical\quad Sections CriticalSections),打印语句执行玩之后再退出临界段,这是因为串口打印是被多个任务共享的,如果有多个任务同时在向串口打印字符的话,会造成打印出的字符顺序混乱。

static EventGroupHandle_t Event_Handle =NULL;/* Definitions for the event bits in the event group. */
#define TASK_A_BIT   ( 1UL << 0UL )  /* Event bit 0, which is set by task A. */
#define TASK_B_BIT   ( 1UL << 5UL )  /* Event bit 5, which is set by task B. */
#define TASK_C_BIT   ( 1UL << 9UL )  /* Event bit 9, which is set by task C. */
#define TASK_D_BIT   ( 1UL << 13UL ) /* Event bit 13,which is set by task D. */static void vSyncingTask( void *pvParameters )
{	EventBits_t uxThisTasksSyncBit;const EventBits_t uxAllSyncBits = ( TASK_A_BIT | TASK_B_BIT | TASK_C_BIT | TASK_D_BIT );/* Four instances of this task are created - each task uses a different eventbit in the synchronization. The event bit to use is passed into each taskinstance using the task parameter. Store it in the uxThisTasksSyncBitvariable. */uxThisTasksSyncBit = ( EventBits_t ) pvParameters;	while (1){/* Simulate this task taking some time to perform an action by delaying for aperiod of time. Each task's delay time is different.This prevents all four instances of this task reachingthe synchronization point at the same time, and so allows the example's behavior to be observed more easily. */if(uxThisTasksSyncBit==((EventBits_t)(TASK_A_BIT))){vTaskDelay(pdMS_TO_TICKS(1000));						}else if(uxThisTasksSyncBit==((EventBits_t)(TASK_B_BIT))){vTaskDelay(pdMS_TO_TICKS(2000));								}				else if(uxThisTasksSyncBit==((EventBits_t)(TASK_C_BIT))){vTaskDelay(pdMS_TO_TICKS(5000));								}else if(uxThisTasksSyncBit==((EventBits_t)(TASK_D_BIT))){vTaskDelay(pdMS_TO_TICKS(10000));								}					/* Print out a message to show this task has reached its synchronizationpoint. pcTaskGetTaskName() is an API function that returns the name assignedto the task when the task was created. */taskENTER_CRITICAL();				printf("%s reached sync point\r\n",pcTaskGetTaskName( NULL ));taskEXIT_CRITICAL();				/* Wait for all the tasks to have reached their respective synchronization points. */xEventGroupSync( Event_Handle,/* The event group used to synchronize. */ uxThisTasksSyncBit, /* The bit set by this task to indicate it has reached the synchronization point. */ uxAllSyncBits,/* The bits to wait for, one bit for each task taking part in the synchronization. */portMAX_DELAY  /* Wait indefinitely for all three tasks to reach the synchronization point. */);/* Print out a message to show this task has passed its synchronizationpoint. As an indefinite delay was used the following line will only beexecuted after all the tasks reached their respective synchronizationpoints. */	taskENTER_CRITICAL();				printf("%s exited sync point\r\n",pcTaskGetTaskName( NULL ));taskEXIT_CRITICAL();}	
}int main(void)
{	 NVIC_PriorityGroupConfig( NVIC_PriorityGroup_4);KEY_Init();uart_init(115200);	printf("Event groups demo start.\r\n");/* Before an event group can be used it must first be created. */Event_Handle = xEventGroupCreate();/* Create three instances of the task. Each task is given a different name,which is later printed out to give a visual indication of which task isexecuting. The event bit to use when the task reaches its synchronization pointis passed into the task using the task parameter. */xTaskCreate( vSyncingTask, "Task A", 1000, (void * const)(TASK_A_BIT), 1, NULL );xTaskCreate( vSyncingTask, "Task B", 1000, (void * const)(TASK_B_BIT), 1, NULL );xTaskCreate( vSyncingTask, "Task C", 1000, (void * const)(TASK_C_BIT), 1, NULL );xTaskCreate( vSyncingTask, "Task D", 1000, (void * const)(TASK_D_BIT), 1, NULL );	/* Start the scheduler so the created tasks start executing. */vTaskStartScheduler();/* If all is well then main() will never reach here as the scheduler willnow be running the tasks. If main() does reach here then it is likely thatthere was insufficient heap memory available for the idle task to be created.Chapter 2 provides more information on heap memory management. */while(1);   	
}

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

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

相关文章

Day15-Linux系统特殊权限知识精讲

Day15-Linux系统特殊权限知识精讲 为什么创建文件默认权限为644&#xff0c;目录为755&#xff1f;&#xff1f;&#xff1f; umask 权限掩码。控制系统的文件和目录的默认权限。 [rootoldboy oldboy]# umask 0022针对文件来说&#xff1a;默认权限计算方法 6 6 6 0 2 2 - …

为什么0.1+0.2不等于0.3

一、JS内部的计算是以二进制形式进行的 js里整数和小数转为二进制形式的方法是不一样的&#xff1a; 二、Number类型使用IEEE754标准64位存储 双精度浮点数&#xff08;double类型&#xff09;为每个数分配64位空间&#xff0c;并以科学计数法的方式存储&#xff1a; 那么对于…

C++入门学习(三十三)函数的定义,两数之和函数作为例子

为什么使用函数&#xff1a;将一些重复的代码封装&#xff0c;方便以后的使用&#xff0c;直接调用即可。 先给一个例子&#xff1a; // 函数返回整数类型 int addNumbers(int a, int b) { // 函数体 int sum a b; return sum; // 返回计算得到的和 } 返回值类型&…

基于卷积神经网络的图像去噪

目录 背影 卷积神经网络CNN的原理 卷积神经网络CNN的定义 卷积神经网络CNN的神经元 卷积神经网络CNN的激活函数 卷积神经网络CNN的传递函数 基于卷积神经网络的图像去噪 完整代码:基于卷积神经网络的图像去噪.rar资源-CSDN文库 https://download.csdn.net/download/abc9918351…

(全注解开发)学习Spring-MVC的第三天

全注解开发 第一部分 : 1.1 消除spring-mvc.xml 这些是原来spring-mvc.xml配置文件的内容 <!--1、组件扫描, 使Controller可以被扫描到--><context:component-scan base-package"com.itheima.controller"/><!--2、非自定义的Bean, 文件上传解析器--&…

mysql-多表查询-外连接

一、外连接查询语法 &#xff08;1&#xff09;左外连接 select 所要查询的内容 from 左表 left outer join 右表 on 条件; &#xff08;2&#xff09;右外连接 select 所要查询的内容 from 左表 right outer join 右表 on 条件; 二、示例 用以下两张表示例 左外连接 右外…

Pyglet综合应用|推箱子游戏之关卡图片载入内存

目录 读取图片 分割图片 综合应用 本篇为之前写的博客《怎样使用Pyglet库给推箱子游戏画关卡地图》的续篇&#xff0c;内容上有相关性&#xff0c;需要阅读的请见链接&#xff1a; https://hannyang.blog.csdn.net/article/details/136209138 「推箱子」是一款风靡全球的益…

system V 共享内存

1.共享内存的原理 要理解共享内存的原理&#xff0c;首先我们得记起进程间通信的前提&#xff1a;必须让不同的进程看到同一份资源&#xff08;必须由OS提供&#xff09; 我们都知道进程都会有自己的进程地址空间&#xff0c;然后都会通过页表与物理内存进行映射&#xff0c;…

图纸透明加密:保护机械图纸安全的新方法

随着信息技术的不断发展&#xff0c;机械制造行业对于图纸安全的需求越来越高。机械图纸是企业的核心竞争力之一&#xff0c;泄露可能导致严重的商业损失和技术风险。为了解决这一问题&#xff0c;图纸透明加密成为了一种新的保护机械图纸安全的方法。本文将介绍图纸透明加密的…

面试字节跳动,我被怼的好狠,怎一个惨字了得

人们都说&#xff0c;这个世界上有两种人注定单身&#xff0c;一种是太优秀的&#xff0c;另一种是太平凡的。 我一听呀&#xff1f;那我这岂不是就不优秀了吗&#xff0c;于是毅然决然和女朋友分了手。 人们都说&#xff0c;互联网寒冬来了&#xff0c;这个时候还在大面积招人…

从零开始手写mmo游戏从框架到爆炸(二十一)— 战斗系统二

导航&#xff1a;从零开始手写mmo游戏从框架到爆炸&#xff08;零&#xff09;—— 导航-CSDN博客 上一章&#xff08;从零开始手写mmo游戏从框架到爆炸&#xff08;二十&#xff09;— 战斗系统一-CSDN博客&#xff09;我们只是完成了基本的战斗&#xff0c;速度属性并没有…

R语言数据分析(三)

R语言数据分析&#xff08;三&#xff09; 文章目录 R语言数据分析&#xff08;三&#xff09;一、可视化步骤1.1 创建ggplot1.2 添加美学和图层1.3 简化代码 二、可视化分布2.1 分类变量2.2 数值变量 三、可视化关系3.1 数值变量和分类变量3.2 两个分类变量3.3 两个数值变量3.…

虚拟机器centos7无法识别yum 命令异常处理笔记

问题现象 启动虚拟机后执行ipconfig 提示未找到该命令,然后执行yum install -y net-tools提示 curl#6 - "Could not resolve host: mirrorlist.centos.org; 未知的错误"的错误 [roothaqdoop~]# ifconfig -bash: ifconfig: 未找到命令 [roothadoop~]# yum install …

超级抽象的前端2

vue3的调用方法失败的原因 function validateConfirm(rule, value, callback) {if (value ! form.password) {callback(new Error(两次输入的密码不一致))} else {callback()}function showAgreement() {dialogVisible.value true}function submitForm() {// 这里是提交表单的…

逆推求期望DP

我的开始的想法&#xff1a;状态设置 dp[i][j] 为玩了 i 个职业 j 个阵营的期望天数&#xff0c;初始值很好解决 dp[1][1]1 &#xff0c;但是有个问题&#xff0c;每对 (i,j) 除了边界那里&#xff0c;会由三个地方来决定这一个(i-1,j-1)(i,j-1)(i-1,j)&#xff0c;所以&#x…

【spring】 ApplicationListener的使用及原理简析

文章目录 使用示例&#xff1a;原理简析&#xff1a; 前言&#xff1a;ApplicationListener 是spring提供的一个监听器&#xff0c;它可以实现一个简单的发布-订阅功能&#xff0c;用有点外行但最简单通俗的话来解释&#xff1a;监听到主业务在执行到了某个节点之后&#xff0c…

【ACM出版】第五届计算机信息和大数据应用国际学术会议(CIBDA 2024)

第五届计算机信息和大数据应用国际学术会议&#xff08;CIBDA 2024&#xff09; 2024 5th International Conference on Computer Information and Big Data Applications 重要信息 大会官网&#xff1a;www.ic-cibda.org 大会时间&#xff1a;2024年3月22-24日 大会地点&#…

Atcoder ABC341 A-D题解

比赛链接:ABC341 Problem A: 先签个到。 #include <bits/stdc.h> using namespace std; int main() {int n;cin>>n;for(int i0;i<n;i)cout<<"10"<<endl;cout<<"1"<<endl;return 0; } Problem B: 继续签。 #i…

week04day03(爬虫 beautifulsoup4、)

一. 使用bs4解析网页 下载bs4 - pip install beautifulsoup4 使用的时候 import bs4专门用于解析网页的第三方库 在使用bs4的时候往往会依赖另一个库lxml pip install lxml 网页代码 <!DOCTYPE html> <html><head><meta charset"utf-8"><…

【Python笔记-设计模式】对象池模式

一、说明 用于管理对象的生命周期&#xff0c;重用已经创建的对象&#xff0c;从而减少资源消耗和创建对象的开销 (一) 解决问题 主要解决频繁创建和销毁对象所带来的性能开销问题。如数据库连接、线程管理、网络连接等&#xff0c;对象的创建和销毁成本相对较高&#xff0c…