让我们看看信号量在实际中的应用。在本练习中,我们将初始化一个信号量,其计数属性将反映系统中特定资源的实例数量。将有一个消费者线程想要消耗该资源,以及一个生产者线程负责生产该资源。每当消费者线程尝试获取访问权限时,我们都会递减并打印计数值;每当生产者线程放弃对实例的访问权限时,我们都会递增并打印计数值。
1. 在本课程的 GitHub 仓库中,打开此练习的基础代码,位于 l8/l8_e1。
2. 将生产者和消费者线程的优先级设置为相等。
#define PRODUCER_PRIORITY 5
#define CONSUMER_PRIORITY 5C3. 将受限资源的实例数量初始化为 10。在本练习中,了解具体是什么资源并不重要(它可以是打印机、传真机、外设、时钟等任何东西),只需假设我们有 10 个该资源的实例。
volatile uint32_t available_instance_count = 10;C4. 创建生产者线程,该线程仅释放资源而不进行任何检查,并休眠随机时间(0-9 毫秒)。当它从休眠中醒来时,会在循环中无限重复此步骤。
void producer(void)
{
printk("Producer thread started\n");
while (1) {
release_access();
k_msleep(sys_rand32_get() % 10);
}
}C5. 创建消费者线程,该线程在不进行任何检查的情况下获取资源访问权限,并假设在进入随机时间(0-9 毫秒)的休眠之前已获得访问权限。当它醒来时,会在循环中无限重复此步骤。
void consumer(void)
{
printk("Consumer thread started\n");
while (1) {
get_access();
k_msleep(sys_rand32_get() % 10);
}
}C6.1 在模拟获取资源的函数 get_access() 中,递减可用资源。
available_instance_count--;
printk("Resource taken and available_instance_count = %d\n", available_instance_count);C6.2 在模拟释放资源的函数 release_access() 中,递增可用资源。然后,在两个函数中都打印 available_instance_count。
available_instance_count++;
printk("Resource given and available_instance_count = %d\n", available_instance_count);C7. 构建应用程序并将其烧录到您的开发套件中。使用串口终端(如集成的 nRF Terminal 或 PuTTY),您应该会看到以下输出:
Resource given and available_instance_count = 54
Resource taken and available_instance_count = 53
Resource given and available_instance_count = 54
Resource taken and available_instance_count = 53
Resource given and available_instance_count = 54
Resource taken and available_instance_count = 53
Resource given and available_instance_count = 54
Resource taken and available_instance_count = 53
Resource given and available_instance_count = 54
Resource given and available_instance_count = 55
Resource taken and available_instance_count = 54
Resource given and available_instance_count = 55Terminal如您所见,打印的可用实例计数大于 10。对于我们仅有 10 个的物理资源来说,这不应该发生。
但使用信号量可以轻松修复此问题,且应用程序无需承担原子性检查/验证可用资源内部计数的负担。
9. 添加一个信号量,首先在定义消费者和生产者优先级之后,使用 K_SEM_DEFINE() 定义信号量,如下所示。
K_SEM_DEFINE(instance_monitor_sem, 10, 10);C10.1 获取资源访问权限时获取信号量。
将函数 get_access() 替换为以下代码片段。我们使用 k_sem_take() 来获取信号量。然后使用 k_sem_count_get() 获取当前的信号量计数。
k_sem_take(&instance_monitor_sem, K_FOREVER);
printk("Resource taken and available_instance_count = %d\n", k_sem_count_get(&instance_monitor_sem));C10.2 释放资源访问权限时给出信号量。
将函数 release_access() 替换为以下代码片段。我们使用 k_sem_give() 来释放信号量。然后使用 k_sem_count_get() 获取当前的信号量计数。
k_sem_give(&instance_monitor_sem);
printk("Resource given and available_instance_count = %d\n", k_sem_count_get(&instance_monitor_sem));C11. 注释掉第 3 步中 available_instance_count 的定义。
大功告成。您现在已经定义并使用了信号量。
11. 构建应用程序并将其烧录到您的开发套件中。使用串口终端(如集成的 nRF Terminal 或 PuTTY),您应该会看到以下输出:
*** Booting nRF Connect SDK ***
Consumer thread started
Resource taken and available_instance_count = 9
Producer thread started
Resource given and available_instance_count Resource taken and available_instance_count = 9
= 10
Resource given and available_instance_count = 10
Resource taken and available_instance_count = 9
Resource given and available_instanResource taken and available_instance_count = 9
ce_count = 10
Resource given and available_Resource taken and available_instance_count = 9
instance_count = 10
Resource given and available_instance_count = 10
Resource taken and available_instance_count = 9
Resource taken and available_instance_count = 8
Resource given and available_instance_count = 9
Resource taken and available_instance_count = 8
Resource given and available_instance_count = 9Resource taken and available_instance_count = 8
Resource taken and available_instance_count = 7
Resource taken and available_instance_count = 6
Resource given and available_instance_count = 7Resource taken and available_instance_count = 6
Resource taken and available_instance_count = 5
Resource taken and available_instance_count = 4
Resource given and available_instance_count = 5
Resource taken and available_instance_count = 4
Resource given and available_instance_count = Resource taken and available_instance_count = 4
5
ResourcResource taken and available_instance_count = 4
e given and available_instance_count = 5
Resource taken and available_instance_count = 3
Resource givResource taken and available_instance_count = 3
en and available_instance_count = 4
Resource taken and available_instance_count = 2
Resource taken and available_instance_count = 1
Resource given and available_instance_count = Resource taken and available_instance_count = 1
2
ResourcResource taken and available_instance_count = 1
e given and available_instance_count = 2
Resource taken and available_instance_count = 0
Resource given and available_instance_count = 1
Resource taken and available_instance_count = 0
Resource given and available_instance_count = 1
Resource taken and available_instance_count = 0
Resource given and available_instance_count = 1
Resource taken and available_instance_count = 0
Terminal您可以看到消费者开始访问所有 10 个资源,而生产者线程同时创建资源,使生产保持平衡。
此练习的解决方案可以在 GitHub 仓库中找到, l8/l8_e1_sol.