9 事件组¶
9.1 章节简介和范围¶
人们已经注意到,实时嵌入式系统必须采取 响应事件的行动。前面的章节已经描述了功能 允许将事件传递给任务的 FreeRTOS。的例子 这些功能包括信号量和队列,两者都具有 以下属性:
-
它们允许任务在阻塞状态下等待单个事件 发生。
-
当事件发生时,他们解锁单个任务。任务就是 解锁是正在等待的最高优先级任务 事件。
事件组是 FreeRTOS 的另一个功能,它允许事件 传达任务。与队列和信号量不同:
-
事件组允许任务在阻塞状态下等待 要发生的多个事件之一的组合。
-
事件组解锁所有等待相同任务的任务 事件或事件的组合,当事件发生时。
事件组的这些独特属性使它们非常有用 同步多个任务,向多个任务广播事件, 允许任务在阻塞状态下等待一组中的任何一个 事件发生,并允许任务在阻塞状态下等待 需要完成多个动作。
事件组还提供了减少 RAM 使用的机会 应用程序,因为通常可以替换许多二进制信号量 与单个事件组。
事件组功能是可选的。包含事件组 功能,构建 FreeRTOS 源文件 event_groups.c 作为 你的项目。
9.1.1 范围¶
本章旨在让读者更好地理解:
- 事件组的实际用途。
- 活动组相对于其他组的优点和缺点 FreeRTOS 功能。
- 如何设置事件组中的位。
- 如何在阻塞状态下等待事件中的位被设置 组。
- 如何使用事件组来同步一组任务。
9.2 事件组的特征¶
9.2.1 事件组、事件标志和事件位¶
事件“标志”是一个 Boolean (1 or 0) 值,用于指示事件是否 发生或未发生。事件“组”是一组事件标志。
事件标志只能是 1 或 0,允许事件标志的状态
被存储在单个位中,并且所有事件标志的状态在一个
事件组存储在单个变量中;每个事件的状态
事件组中的标志由变量中的单个位表示
型号 EventBits_t。因此,事件标志也称为事件
“位”。如果 EventBits_t 变量中的某个位设置为 1,则该事件
该位代表的情况已经发生。如果某个位设置为 0
EventBits_t变量,则该位代表的事件还没有
发生。
图 9.1 显示了各个事件标志如何映射到各个位
在 EventBits_t 类型的变量中。
图 9.1 EventBits_t 类型变量中事件标志到位数的映射
例如,如果事件组的值为 0x92(二进制 1001 0010)
那么只有事件位 1、4 和 7 被设置,因此只有代表的事件
位 1、4 和 7 已发生。图 9.2 显示了一个变量类型
EventBits_t 已设置事件位 1、4 和 7,以及所有其他事件
位清零,为事件组赋予值 0x92。
图 9.2 事件组中仅设置了位 1、4 和 7,所有其他事件标志均被清除,使得事件组的值为 0x92
由应用程序编写者为个人赋予含义 事件组内的位。例如,应用程序编写者可能 创建一个事件组,然后:
-
定义事件组中的位 0 表示“消息已被发送” 从网络收到”。
-
定义事件组中的位 1 表示“消息已准备好 被发送到网络上”。
-
定义事件组中的位 2 表示“中止当前事件” 网络连接”。
9.2.2 有关 EventBits_t 数据类型的更多信息¶
事件组中事件位的数量取决于
configTICK_TYPE_WIDTH_IN_BITS 编译时配置常量
FreeRTOSConfig.h[^24]:
[^24]:configTICK_TYPE_WIDTH_IN_BITS 配置用于保存 RTOS 的类型
滴答计数,因此似乎与事件组功能无关。其
对 EventBits_t 类型的影响是 FreeRTOS 的结果
内部实现,并且需要设置 configTICK_TYPE_WIDTH_IN_BITS
对于 TICK_TYPE_WIDTH_16_BITS,仅当 FreeRTOS 在
可以更有效地处理 16 位类型的架构
32 位类型。
-
如果
configTICK_TYPE_WIDTH_IN_BITS是TICK_TYPE_WIDTH_16_BITS,则每个 事件组包含 8 个可用事件位。 -
如果
configTICK_TYPE_WIDTH_IN_BITS为TICK_TYPE_WIDTH_32_BITS,则每个事件组包含 24 个 可用的事件位。 -
如果
configTICK_TYPE_WIDTH_IN_BITS是TICK_TYPE_WIDTH_64_BITS,则每个 事件组包含 56 个可用事件位。
9.2.3 多任务访问¶
事件组本身就是对象,任何人都可以访问 任务或 ISR 知道它们的存在。可以设置任意数量的任务 同一事件组中的位,并且任意数量的任务可以读取位 同一事件组。
9.2.4 使用事件组的实际示例¶
FreeRTOS+TCP TCP/IP堆栈的实现提供了实用的 如何使用事件组同时简化事件的示例 设计,并最大限度地减少资源使用。
TCP 套接字必须响应许多不同的事件。事件示例 包括接受事件、绑定事件、读取事件和关闭事件。的 套接字在任何给定时间可以预期的事件取决于状态 插座的。例如,如果套接字已创建,但尚未创建 绑定到一个地址,那么它可以期望收到一个绑定事件,但是 不会期望收到读取事件(如果收到,则无法读取数据) 没有地址)。
FreeRTOS+TCP 套接字的状态保存在名为
FreeRTOS_Socket_t。该结构包含一个事件组,该事件组具有
为套接字必须处理的每个事件定义的事件位。 FreeRTOS+TCP
API 调用该块来等待一个事件或一组事件,只需
阻止事件组。
事件组还包含一个“中止”位,允许 TCP 连接 被中止,无论套接字正在等待哪个事件 时间。
9.3 使用事件组进行事件管理¶
9.3.1 xEventGroupCreate() API 功能¶
FreeRTOS还包括xEventGroupCreateStatic()函数,
它静态分配创建事件组所需的内存
在编译时:必须先显式创建事件组,然后才能
被使用。
使用 EventGroupHandle_t 类型的变量引用事件组。
xEventGroupCreate() API函数用于创建事件组,
并返回 EventGroupHandle_t 来引用事件组
创造。
xEventGroupCreate() 返回值
- 返回值
如果返回NULL,则无法创建事件组 因为 FreeRTOS 没有足够的可用堆内存 分配事件组数据结构。第 3 章提供了更多内容 有关堆内存管理的信息。
返回的非 NULL 值表示事件组已 已创建成功。返回值应存储为 创建的事件组的句柄。
9.3.2 xEventGroupSetBits() API 功能¶
xEventGroupSetBits() API 函数设置事件中的一个或多个位
组,通常用于通知任务该事件
由一个或多个位表示的设置已经发生。
注意:切勿从中断服务中调用
xEventGroupSetBits()常规。中断安全版本xEventGroupSetBitsFromISR()应 在其位置使用。
xEventGroupSetBits()参数及返回值
xEventGroup
正在设置位的事件组的句柄。活动内容
组句柄将从调用中返回
xEventGroupCreate() 用于创建事件组。
uxBitsToSet
指定要设置的一个或多个事件位的位掩码
活动组中1人。事件组的值按位更新
将事件组的现有值与传入的值进行“或”运算
uxBitsToSet。
例如,将 uxBitsToSet 设置为 0x04(二进制 0100)将导致
在事件组中的事件位 3 中(如果尚未设置)
设置),同时保留事件组中的所有其他事件位
不变。
- 返回值
调用时事件组的值
xEventGroupSetBits() 已返回。请注意,返回的值不会
必须设置 uxBitsToSet 指定的位,因为这些位
可能已被不同的任务再次清除。
9.3.3 xEventGroupSetBitsFromISR() API 功能¶
xEventGroupSetBitsFromISR() 是中断安全版本
xEventGroupSetBits()。
给出信号量是一种确定性操作,因为它在 提出给予信号量最多可以导致一个任务离开 阻塞状态。事件组中的位何时设置是未知的 提前有多少任务将离开阻塞状态,因此设置位 在事件组中不是确定性操作。
FreeRTOS设计和实现标准不允许
在中断服务内执行的非确定性操作
例程,或者当中断被禁用时。由于这个原因,
xEventGroupSetBitsFromISR() 不会直接在内部设置事件位
中断服务例程,而是将操作推迟到 RTOS
守护进程任务。
xEventGroupSetBitsFromISR()参数及返回值
xEventGroup
正在设置位的事件组的句柄。活动内容
组句柄将从调用中返回
xEventGroupCreate() 用于创建事件组。
uxBitsToSet
指定要设置的一个或多个事件位的位掩码
活动组中1人。事件组的值按位更新
将事件组的现有值与传入的值进行“或”运算
uxBitsToSet。
例如,将 uxBitsToSet 设置为 0x05(二进制 0101)将导致
事件组中的事件位 2 和事件位 0 被置位(如果它们
尚未设置),同时保留所有其他事件位
事件组不变。
pxHigherPriorityTaskWoken
xEventGroupSetBitsFromISR() 不直接设置事件位
在中断服务程序内部,而是将操作推迟到
RTOS 守护程序任务通过在计时器命令队列上发送命令来执行。如果
守护任务处于阻塞状态,等待数据变为
在定时器命令队列上可用,然后写入定时器命令
队列将导致守护任务离开阻塞状态。如果
守护任务的优先级高于当前任务的优先级
执行 task (the task that was interrupted),然后在内部,
xEventGroupSetBitsFromISR() 将 *pxHigherPriorityTaskWoken 设置为
pdTRUE。
如果 xEventGroupSetBitsFromISR() 将此值设置为 pdTRUE,则
上下文切换应该在中断退出之前执行。这个
将确保中断直接返回到守护程序任务,如下所示
守护任务将是最高优先级的就绪状态任务。
- 返回值
有两种可能的返回值:
-
仅当数据成功发送至 定时器命令队列。
-
如果无法执行“设置位”命令,将返回
pdFALSE写入定时器命令队列,因为该队列已经 满。
9.3.4 xEventGroupWaitBits() API 功能¶
xEventGroupWaitBits() API 函数允许任务读取值
事件组的一个,并且可以选择在阻塞状态下等待一个或
如果事件位已设置,则事件组中的更多事件位将被设置
尚未设置。
调度程序用来确定任务是否进入的条件
阻塞状态,以及任务何时离开阻塞状态,是
称为“解锁条件”。解锁条件由一个指定
uxBitsToWaitFor 和 xWaitForAllBits 参数的组合
价值观:
-
uxBitsToWaitFor指定事件组中的哪些事件位 测试 -
xWaitForAllBits指定是使用按位 OR 测试还是使用 按位 AND 测试
如果满足解锁条件,任务不会进入阻塞状态
调用 xEventGroupWaitBits() 时。
导致任务进入的条件示例 表 6 中提供了阻塞状态或退出阻塞状态。 表6仅显示了事件的最低有效的四个二进制位 组和 uxBitsToWaitFor 值 - 这两个值的其他位是 假设为零。
| 现有事件组值 | uxBitsToWaitFor值 | xWaitForAllBits 值 | 结果行为 |
|---|---|---|---|
| 0000 | 0000 0101 | pdFALSE | 由于事件组中的位 0 或位 2 均未置位,调用任务将进入阻塞状态,并且当事件组中的位 0 OR 位 2 置位时,调用任务将离开阻塞状态。 |
| 0100 | 0100 0101 | pdTRUE | 调用任务将进入阻塞状态,因为事件组中的位 0 和位 2 未同时置位,并且当事件组中的位 0 AND 和位 2 均置位时,调用任务将离开阻塞状态。 |
| 0100 | 0100 0110 | pdFALSE | 调用任务不会进入阻塞状态,因为 xWaitForAllBits 是 pdFALSE,并且 uxBitsToWaitFor 指定的两位之一已在事件组中设置。 |
| 0100 | 0100 0110 | pdTRUE | 调用任务将进入阻塞状态,因为 xWaitForAllBits 是 pdTRUE,并且事件组中仅设置了 uxBitsToWaitFor 指定的两位之一。当事件组中的位 1 和位 2 均被设置时,任务将离开阻塞状态。 |
表 6 uxBitsToWaitFor 和 xWaitForAllBits 参数的影响
调用任务指定要使用 uxBitsToWaitFor 进行测试的位
参数,并且调用任务可能需要清除这些
满足解锁条件后,位恢复为零。事件位
可以使用 xEventGroupClearBits() API 函数清除,但使用
手动清除事件位的功能将导致竞争条件
在应用程序代码中,如果:
- 多个任务使用同一事件组。
- 事件组中的位由不同的任务或由 中断服务程序。
提供 xClearOnExit 参数是为了避免这些潜在的竞争
条件。如果 xClearOnExit 设置为 pdTRUE,则测试和
事件位的清除对于调用任务来说似乎是一个原子操作
operation (uninterruptable by other tasks or interrupts)。
xEventGroupWaitBits()参数及返回值
xEventGroup
包含事件位的事件组的句柄
阅读。事件组句柄将从调用中返回
xEventGroupCreate() 用于创建事件组。
uxBitsToWaitFor
指定要测试的一个或多个事件位的位掩码 在活动组中。
例如,如果调用任务想要等待事件位 0 和/或
事件位 2 在事件组中设置,然后设置 uxBitsToWaitFor
到 0x05(二进制 0101)。更多示例请参阅表 6。
xClearOnExit
如果满足调用任务的解锁条件,并且
xClearOnExit 设置为 pdTRUE,则事件位由
uxBitsToWaitFor 将在事件组中清零之前
调用任务退出 xEventGroupWaitBits() API 函数。
如果 xClearOnExit 设置为 pdFALSE,则事件位的状态
事件组中的 xEventGroupWaitBits() 未修改 API
功能。
xWaitForAllBits
uxBitsToWaitFor 参数指定要测试的事件位
事件组。 xWaitForAllBits 指定调用任务是否应该
当一个或多个事件位发生时,从阻塞状态中移除
由 uxBitsToWaitFor 参数指定的设置,或者仅当所有
设置 uxBitsToWaitFor 参数指定的事件位。
如果 xWaitForAllBits 设置为 pdFALSE,则进入
阻塞状态等待其解除阻塞条件满足将离开
当 uxBitsToWaitFor 指定的任何位变为阻塞状态时
set (or the timeout specified by the xTicksToWait parameter expires)。
如果 xWaitForAllBits 设置为 pdTRUE,则进入
阻塞状态等待其解锁条件满足只会
当指定的所有位都离开阻塞状态时
uxBitsToWaitFor 已设置(或 xTicksToWait 指定的超时
参数过期)。
示例请参阅表 6。
xTicksToWait
任务应保留在已阻止状态的最长时间 状态等待满足其解锁条件。
如果 xTicksToWait 为 xEventGroupWaitBits() 将立即返回
零,或者在xEventGroupWaitBits()时刻满足解锁条件
被称为。
区块时间以滴答周期为单位指定,因此它的绝对时间
表示取决于滴答频率。宏 pdMS_TO_TICKS()
可用于将以毫秒为单位的时间转换为时间
以刻度指定。
将 xTicksToWait 设置为 portMAX_DELAY 将导致任务等待
indefinitely (without timing out),前提是设置了 INCLUDE_vTaskSuspend
为 1 英寸 FreeRTOSConfig.h。
- 返回值
如果 xEventGroupWaitBits() 由于调用任务的
满足解锁条件,则返回值为
调用任务的解锁条件时的事件组为 met
(before any bits were automatically cleared if xClearOnExit was pdTRUE)。
在这种情况下,返回值也将满足解锁条件。
如果 xEventGroupWaitBits() 由于指定的区块时间而返回
xTicksToWait参数过期,则返回值为该值
块时间到期时事件组的编号。在这种情况下
返回值将不满足解锁条件。
9.3.5 xEventGroupGetStaticBuffer() API 功能¶
xEventGroupGetStaticBuffer() API 函数提供了检索指针的方法
到静态创建的事件组的缓冲区。它与提供的缓冲区相同
在创建事件组时。
*注意:切勿从中断服务中调用 xEventGroupGetStaticBuffer()
例行公事。
xEventGroupGetStaticBuffer()参数及返回值
xEventGroup
要检索缓冲区的事件组。此活动组必须
由 xEventGroupCreateStatic() 创建。
ppxEventGroupBuffer
用于返回指向事件组数据结构缓冲区的指针。 它与创建时提供的缓冲区相同。
- 返回值
有两种可能的返回值:
-
如果成功检索缓冲区,将返回
pdTRUE。 -
如果缓冲区未成功检索,将返回
pdFALSE。
¶
示例 9.1 使用事件组进行实验
此示例演示如何:
- 创建一个活动组。
- 通过中断服务例程设置事件组中的位。
- 设置任务中事件组中的位。
- 阻止事件组。
xEventGroupWaitBits() xWaitForAllBits参数的作用是
首先执行示例并将 xWaitForAllBits 设置为
pdFALSE,然后执行示例并将 xWaitForAllBits 设置为
pdTRUE。
事件位 0 和事件位 1 由任务设置。事件位 2 设置为 一个中断服务程序。这三位给出了描述性的 使用清单 9.6 中所示的 #define 语句命名。
清单 9.7 显示了设置事件位 0 的任务的实现
和事件位 1。它处于一个循环中,重复设置一位,然后
其他,每次调用之间有 200 毫秒的延迟
xEventGroupSetBits()。在将每个位设置为之前打印出一个字符串
允许在控制台中看到执行顺序。
清单9.8显示了中断服务程序的实现
设置事件组中的位 2。再次打印出一个字符串
在设置该位以允许执行序列之前
控制台。然而在这种情况下,因为控制台输出不应该是
直接在中断服务程序中执行,
xTimerPendFunctionCallFromISR() 用于执行输出
RTOS 守护程序任务的上下文。
与前面的示例一样,中断服务例程由 强制软件中断的简单周期性任务。在这个例子中, 中断每 500 毫秒生成一次。
清单 9.9 显示了调用的任务的实现
xEventGroupWaitBits() 用于阻止事件组。该任务打印出一个
事件组中设置的每个位的字符串。
xEventGroupWaitBits() xClearOnExit 参数设置为 pdTRUE,因此
导致调用 xEventGroupWaitBits() 的一个或多个事件位
返回值在 xEventGroupWaitBits() 之前会自动清零
返回。
main() 函数在之前创建事件组和任务
启动调度程序。其实现请参见清单 9.10。的
从事件组读取的任务的优先级高于
写入事件组的任务的优先级,确保
每次读任务执行时,读任务都会抢占写任务
满足解锁条件。
使用以下命令执行例 9.1 时产生的输出
显示 xEventGroupWaitBits() xWaitForAllBits 参数设置为 pdFALSE
如图 9.3 所示。从图 9.3 中可以看出,由于
xWaitForAllBits 调用 xEventGroupWaitBits() 中的参数已设置
到 pdFALSE,从事件组读取的任务离开阻塞状态
状态并在每次设置任何事件位时立即执行。
图 9.3 在 xWaitForAllBits 设置为 pdFALSE 的情况下执行示例 9.1 时产生的输出
使用以下命令执行例 9.1 时产生的输出
显示 xEventGroupWaitBits() xWaitForAllBits 参数设置为 pdTRUE
如图 9.4 所示。从图 9.4 可以看出,由于
xWaitForAllBits 参数设置为 pdTRUE,任务从
事件组仅在所有三个事件之后才离开阻塞状态
事件位已设置。
图 9.4 在 xWaitForAllBits 设置为 pdTRUE 的情况下执行示例 9.1 时产生的输出
9.4 使用事件组进行任务同步¶
有时应用程序的设计需要两个或多个任务来 彼此同步。例如,考虑一个设计,其中任务 A 接收一个事件,然后委托一些必要的处理 将事件分配给其他三个任务:任务 B、任务 C 和任务 D。如果任务 A 在任务 B、C 和 D 全部完成之前无法接收另一个事件 处理前一个事件,那么所有四个任务都需要 彼此同步。每个任务的同步点将是 该任务完成处理后,无法继续进行 直到其他每个任务都完成相同的操作。任务A只能 在所有四个任务都完成后接收另一个事件 同步点。
此类任务需求的一个不太抽象的示例 同步可以在 FreeRTOS+TCP 演示之一中找到 项目。该演示在两个任务之间共享 TCP 套接字;一 任务向套接字发送数据,另一个任务从套接字接收数据 相同的套接字[^25]。这两个任务关闭 TCP 都是不安全的 套接字,直到确定其他任务不会尝试访问该套接字 再次套接字。如果两个任务中的任何一个希望关闭套接字, 然后它必须将其意图告知其他任务,然后等待 在继续之前停止使用套接字的其他任务。场景 其中任务将数据发送到希望关闭的套接字 清单 9.10 中的伪代码演示了套接字。
[^25]:在撰写本文时,这是单一方法的唯一方法 FreeRTOS+TCP 套接字可以在任务之间共享。
清单 9.10 演示的场景很简单,因为只有 两个任务需要相互同步,但很容易 看看场景将如何变得更加复杂,并需要更多任务 如果其他任务正在执行处理,则加入同步 这取决于套接字是否打开。
事件组可用于创建同步点:
-
每个必须参与同步的任务都被分配一个 事件组内的唯一事件位。
-
每个任务在达到同步时设置自己的事件位 点。
-
设置了自己的事件位后,事件组上的每个任务都会阻塞 等待代表所有其他同步的事件位 任务也已确定。
但是,xEventGroupSetBits() 和 xEventGroupWaitBits() API
在这种情况下不能使用函数。如果使用它们,那么
设置一个位(以指示任务已达到同步
点)和位测试(以确定其他同步
任务已达到其同步点)将作为两个执行
单独的操作。要了解为什么这会成为问题,请考虑
任务 A、任务 B 和任务 C 尝试使用同步的场景
活动组:
-
任务A和任务B已经到达同步点,所以 它们的事件位在事件组中设置,并且它们位于 阻塞状态等待任务 C 的事件位也被设置。
-
任务C到达同步点,并使用
xEventGroupSetBits()设置事件组中的位。一旦 任务 C 的位被设置,任务 A 和任务 B 离开阻塞状态,并且 清除所有三个事件位。
3.任务C然后调用xEventGroupWaitBits()来等待所有三个事件
位被设置,但到那时,所有三个事件位都已设置
已清除,任务A和任务B已离开各自的
同步点,因此同步失败。
要成功使用事件组创建同步点,
事件位的设置以及事件位的后续测试,
必须作为单个不间断操作来执行。的
xEventGroupSync() API 功能就是为此目的而提供的。
9.4.1 xEventGroupSync() API 功能¶
提供 xEventGroupSync() 是为了允许两个或多个任务使用一个事件
组之间相互同步。该函数允许任务设置
事件组中的一个或多个事件位,然后等待组合
事件位在同一事件组中设置为单个
不间断运行。
xEventGroupSync() uxBitsToWaitFor 参数指定调用
任务的解锁条件。 uxBitsToWaitFor 指定的事件位
在 xEventGroupSync() 返回之前将被清零,如果
xEventGroupSync() 由于满足解锁条件而返回。
xEventGroupSync()参数及返回值
xEventGroup
要在其中设置事件位的事件组的句柄,以及
然后进行测试。事件组句柄将从
调用 xEventGroupCreate() 用于创建事件组。
uxBitsToSet
指定要设置的一个或多个事件位的位掩码
活动组中1人。事件组的值按位更新
将事件组的现有值与传入的值进行“或”运算
uxBitsToSet。
例如,将 uxBitsToSet 设置为 0x04(二进制 0100)将导致
事件位 2 变为 set (if it was not already set),同时离开
事件组中的所有其他事件位不变。
uxBitsToWaitFor
指定要测试的一个或多个事件位的位掩码 在活动组中。
例如,如果调用任务想要等待事件位 0、1
和 2 在事件组中设置,然后将 uxBitsToWaitFor 设置为 0x07
(二进制 111)。
xTicksToWait
任务应保留在已阻止状态的最长时间 状态等待满足其解锁条件。
如果 xTicksToWait 为零,则 xEventGroupSync() 将立即返回,或者
xEventGroupSync() 时满足解锁条件
叫。
区块时间以滴答周期为单位指定,因此它的绝对时间
表示取决于滴答频率。宏 pdMS_TO_TICKS()
可用于将以毫秒为单位的时间转换为时间
以刻度指定。
将 xTicksToWait 设置为 portMAX_DELAY 将导致任务等待
indefinitely (without timing out),前提是设置了 INCLUDE_vTaskSuspend
为 1 英寸 FreeRTOSConfig.h。
- 返回值
如果 xEventGroupSync() 由于调用任务解锁而返回
满足条件,则返回值为事件的值
满足调用任务的解锁条件时(之前
任何位都会自动清零)。在这种情况下
返回值也将满足调用任务的解锁条件。
如果 xEventGroupSync() 由于指定的区块时间而返回
xTicksToWait参数过期,则返回值为
区块时间到期时的事件组。在这种情况下
返回值不满足调用任务的解锁
条件。
¶
示例 9.2 同步任务
示例 9.2 使用 xEventGroupSync() 同步 a 的三个实例
单一任务执行。任务参数用于传入每个
实例 任务在调用时将设置的事件位
xEventGroupSync()。
该任务在调用 xEventGroupSync() 之前打印一条消息,然后再次
对 xEventGroupSync() 的调用返回后。每条消息包括
时间戳。这允许观察执行顺序
产生的输出。伪随机延迟用于防止所有
任务同时到达同步点。
该任务的实现请参见清单 9.12。
main() 函数创建事件组,创建所有三个任务,
然后启动调度程序。其实现请参见清单 9.14。
执行例 9.2 时产生的输出如图 9.5 所示。 可以看出,即使各个任务达到同步 指向different (pseudo random)时间,每个任务退出 同时同步点[^26](即 最后一个任务到达同步点)。
[^26]:图9.5显示了在FreeRTOS Windows端口中运行的示例, 它不提供真正的实时行为(特别是当 使用 Windows 系统调用打印到控制台),并且将 因此显示出一些时间变化。
