当前位置:网站首页 > 时间管理与高效工作 > 正文

互斥锁和条件变量机制的工作流程(互斥锁和条件变量怎么用)



在本章中,会涉及如下内容:

    9.1 基本概念

    对于整个单片机程序,我们称之为application,应用程序。

    使用FreeRTOS时,我们可以在application中创建多个任务(task),有些文档把任务也称为线程(thread)。

    以日常生活为例,比如这个母亲要同时做两件事:

              9.2 任务创建与删除

              9.2.1 什么是任务

              在FreeRTOS中,任务就是一个函数,原型如下:

              要注意的是:

                    9.2.2 创建任务

                    创建任务时使用的函数如下:

                    参数说明:

                    使用静态分配内存的函数如下:

                    相比于使用动态分配内存创建任务的函数,最后2个参数不一样:

                    9.2.3 示例1: 创建任务

                    代码为: 05_create_task

                    使用动态、静态分配内存的方式,分别创建多个任务:监测遥控器并在LCD上显示、LED闪烁、全彩LED渐变颜色、使用无源蜂鸣器播放音乐。

                    9.2.4 示例2: 使用任务参数

                    代码为:06_create_task_use_params

                    我们说过,多个任务可以使用同一个函数,怎么体现它们的差别?

                      我们创建2个任务,使用同一个函数,但是在LCD上打印不一样的信息。

                      上述代码中的info来自参数pvParameters,pvParameters来自哪里?创建任务时传入的。

                      代码如下:

                        9.2.5 任务的删除

                        删除任务时使用的函数如下:

                        参数说明:

                        怎么删除任务?举个不好的例子:

                          9.2.6 示例3: 删除任务

                          代码为: 07_delete_task

                          功能为:当监测到遥控器的Power按键被按下后,删除音乐播放任务。

                          代码如下:

                          9.3 任务优先级和Tick

                          #

                          9.3.1 任务优先级

                          怎么让播放的音乐更动听?提高优先级。

                          优先级的取值范围是:0~(configMAX_PRIORITIES – 1),数值越大优先级越高。

                          FreeRTOS的调度器可以使用2种方法来快速找出优先级最高的、可以运行的任务。使用不同的方法时,configMAX_PRIORITIES 的取值有所不同。

                            使用C函数实现,对所有的架构都是同样的代码。对configMAX_PRIORITIES的取值没有限制。但是configMAX_PRIORITIES的取值还是尽量小,因为取值越大越浪费内存,也浪费时间。

                            configUSE_PORT_OPTIMISED_TASK_SELECTION被定义为0、或者未定义时,使用此方法。

                              架构相关的汇编指令,可以从一个32位的数里快速地找出为1的最高位。使用这些指令,可以快速找出优先级最高的、可以运行的任务。使用这种方法时,configMAX_PRIORITIES的取值不能超过32。

                              configUSE_PORT_OPTIMISED_TASK_SELECTION被定义为1时,使用此方法。

                              在学习调度方法之前,你只要初略地知道:

                                这无需记忆,就像我们举的例子:

                                  9.3.2 Tick

                                  对于同优先级的任务,它们“轮流”执行。怎么轮流?你执行一会,我执行一会。

                                  "一会"怎么定义?

                                  人有心跳,心跳间隔基本恒定。

                                  FreeRTOS中也有心跳,它使用定时器产生固定间隔的中断。这叫Tick、滴答,比如每10ms发生一次时钟中断。

                                  如下图:

                                    相同优先级的任务怎么切换呢?请看下图:

                                      有了Tick的概念后,我们就可以使用Tick来衡量时间了,比如:

                                      注意,基于Tick实现的延时并不精确,比如vTaskDelay(2)的本意是延迟2个Tick周期,有可能经过1个Tick多一点就返回了。 如下图:

                                      使用vTaskDelay函数时,建议以ms为单位,使用pdMS_TO_TICKS把时间转换为Tick。

                                      这样的代码就与configTICK_RATE_HZ无关,即使配置项configTICK_RATE_HZ改变了,我们也不用去修改代码。

                                      9.3.3 示例4: 优先级实验

                                      代码为:08_task_priority 本程序会:提高音乐播放任务的优先级,使用vTaskDelay进行延时。

                                      代码如下:

                                      9.3.4 修改优先级

                                      使用uxTaskPriorityGet来获得任务的优先级:

                                      使用参数xTask来指定任务,设置为NULL表示获取自己的优先级。

                                      使用vTaskPrioritySet 来设置任务的优先级:

                                      使用参数xTask来指定任务,设置为NULL表示获取自己的优先级。

                                      使用vTaskPrioritySet 来设置任务的优先级:

                                      使用参数xTask来指定任务,设置为NULL表示设置自己的优先级;

                                      参数uxNewPriority表示新的优先级,取值范围是0~(configMAX_PRIORITIES – 1)。

                                      #

                                      9.4 任务状态

                                      以前我们很简单地把任务的状态分为2中:运行(Runing)、非运行(Not Running)。 对于非运行的状态,还可以继续细分,比如前面的FreeRTOS_04_task_priority中:

                                        #

                                        9.4.1 阻塞状态(Blocked)

                                        在日常生活的例子中,母亲在电脑前跟同事沟通时,如果同事一直没回复,那么母亲的工作就被卡住了、被堵住了、处于阻塞状态(Blocked)。重点在于:母亲在等待。

                                        在FreeRTOS_04_task_priority实验中,如果把任务3中的vTaskDelay调用注释掉,那么任务1、任务2根本没有执行的机会,任务1、任务2被"饿死"了(starve)。

                                        在实际产品中,我们不会让一个任务一直运行,而是使用"事件驱动"的方法让它运行:

                                          在阻塞状态的任务,它可以等待两种类型的事件:

                                              在等待一个同步事件时,可以加上超时时间。比如等待队里数据,超时时间设为10ms:

                                                9.4.2 暂停状态(Suspended)

                                                在日常生活的例子中,母亲正在电脑前跟同事沟通,母亲可以暂停:

                                                  FreeRTOS中的任务也可以进入暂停状态,唯一的方法是通过vTaskSuspend函数。函数原型如下:

                                                  参数xTaskToSuspend表示要暂停的任务,如果为NULL,表示暂停自己。

                                                  要退出暂停状态,只能由别人来操作

                                                    实际开发中,暂停状态用得不多。

                                                    9.4.3 就绪状态(Ready)

                                                    这个任务完全准备好了,随时可以运行:只是还轮不到它。这时,它就处于就绪态(Ready)。

                                                    9.4.4 完整的状态转换图

                                                    9.6 Delay函数

                                                    9.6.1 两个Delay函数

                                                    有两个Delay函数:

                                                      这2个函数原型如下:

                                                      下面画图说明:

                                                        所以可以使用xTaskDelayUntil来让任务周期性地运行

                                                        9.6.2 示例5: Delay

                                                        本节代码为:11_taskdelay。 本程序会比较vTaskDelay和vTaskDelayUntil实际阻塞的时间,并在LCD上打印出来。

                                                        代码如下:

                                                        9.7 空闲任务及其钩子函数

                                                        #

                                                        9.7.1 介绍

                                                        空闲任务(Idle任务)的作用之一:释放被删除的任务的内存。

                                                        除了上述目的之外,为什么必须要有空闲任务?一个良好的程序,它的任务都是事件驱动的:平时大部分时间处于阻塞状态。有可能我们自己创建的所有任务都无法执行,但是调度器必须能找到一个可以运行的任务:所以,我们要提供空闲任务。在使用vTaskStartScheduler()函数来创建、启动调度器时,这个函数内部会创建空闲任务:

                                                          空闲任务的优先级为0,这意味着一旦某个用户的任务变为就绪态,那么空闲任务马上被切换出去,让这个用户任务运行。在这种情况下,我们说用户任务"抢占"(pre-empt)了空闲任务,这是由调度器实现的。

                                                          要注意的是:如果使用vTaskDelete()来删除任务,那么你就要确保空闲任务有机会执行,否则就无法释放被删除任务的内存。

                                                          我们可以添加一个空闲任务的钩子函数(Idle Task Hook Functions),空闲任务的循环每执行一次,就会调用一次钩子函数。钩子函数的作用有这些:

                                                            9.7.2 使用钩子函数的前提

                                                            在FreeRTOSSource asks.c中,可以看到如下代码,所以前提就是:

                                                              9.8 调度算法

                                                              #

                                                              9.8.1 重要概念

                                                              这些知识在前面都提到过了,这里总结一下。

                                                              正在运行的任务,被称为"正在使用处理器",它处于运行状态。在单处理系统中,任何时间里只能有一个任务处于运行状态。

                                                              非运行状态的任务,它处于这3中状态之一:阻塞(Blocked)、暂停(Suspended)、就绪(Ready)。就绪态的任务,可以被调度器挑选出来切换为运行状态,调度器永远都是挑选最高优先级的就绪态任务并让它进入运行状态。

                                                              阻塞状态的任务,它在等待"事件",当事件发生时任务就会进入就绪状态。事件分为两类:时间相关的事件、同步事件。所谓时间相关的事件,就是设置超时时间:在指定时间内阻塞,时间到了就进入就绪状态。使用时间相关的事件,可以实现周期性的功能、可以实现超时功能。同步事件就是:某个任务在等待某些信息,别的任务或者中断服务程序会给它发送信息。怎么"发送信息"?方法很多,有:任务通知(task notification)、队列(queue)、事件组(event group)、信号量(semaphoe)、互斥量(mutex)等。这些方法用来发送同步信息,比如表示某个外设得到了数据。

                                                              #

                                                              9.8.2 配置调度算法

                                                              所谓调度算法,就是怎么确定哪个就绪态的任务可以切换为运行状态。

                                                              通过配置文件FreeRTOSConfig.h的两个配置项来配置调度算法:configUSE_PREEMPTION、configUSE_TIME_SLICING。

                                                              还有第三个配置项:configUSE_TICKLESS_IDLE,它是一个高级选项,用于关闭Tick中断来实现省电,后续单独讲解。现在我们假设configUSE_TICKLESS_IDLE被设为0,先不使用这个功能。 调度算法的行为主要体现在两方面:高优先级的任务先运行、同优先级的就绪态任务如何被选中。调度算法要确保同优先级的就绪态任务,能"轮流"运行,策略是"轮转调度"(Round Robin Scheduling)。轮转调度并不保证任务的运行时间是公平分配的,我们还可以细化时间的分配方法。 从3个角度统一理解多种调度算法:

                                                                    注:

                                                                      9.8.3 示例6: 调度

                                                                      9.8.4 对比效果: 抢占与否

                                                                      在 FreeRTOSConfig.h 中,定义这样的宏,对比逻辑分析仪的效果:

                                                                      对比结果为:

                                                                        9.8.5 对比效果: 时间片轮转与否

                                                                        在 FreeRTOSConfig.h 中,定义这样的宏,对比逻辑分析仪的效果:

                                                                        从下面的对比图可以知道:

                                                                          9.8.6 对比效果: 空闲任务让步

                                                                          在 FreeRTOSConfig.h 中,定义这样的宏,对比逻辑分析仪的效果:

                                                                          从下面的对比图可以知道:

                                                                            到此这篇互斥锁和条件变量机制的工作流程(互斥锁和条件变量怎么用)的文章就介绍到这了,更多相关内容请继续浏览下面的相关推荐文章,希望大家都能在编程的领域有一番成就!

                                                                            版权声明


                                                                            相关文章:

                                                                          • w25q128jvsiq电压(ws2812工作电压)2025-06-26 09:22:27
                                                                          • impdp导入dump文件(impdp导入很长时间没有反应)2025-06-26 09:22:27
                                                                          • ad52117功率(ad712最佳工作电压)2025-06-26 09:22:27
                                                                          • impdp导入命令参数详解(impdp导入很长时间没有反应)2025-06-26 09:22:27
                                                                          • keil破解到2032年(keil破解时间2020年到期)2025-06-26 09:22:27
                                                                          • k8s 发布时间(k8s发布时间)2025-06-26 09:22:27
                                                                          • 神奇宝贝查看神兽刷新时间指令(神奇宝贝查看神兽刷新时间指令是什么)2025-06-26 09:22:27
                                                                          • 泰拉瑞亚调整时间的东西(泰拉瑞亚改变时间)2025-06-26 09:22:27
                                                                          • max30100工作原理(max319工作原理)2025-06-26 09:22:27
                                                                          • 华为机考规则(华为机考时间多长)2025-06-26 09:22:27
                                                                          • 全屏图片