FreeRTOS入门教程(信号量的具体使用)

举报
yd_274589494 发表于 2023/10/23 21:07:57 2023/10/23
【摘要】 @TOC 前言本篇文章来为大家讲解信号量的具体使用。 一、使用二值信号量完成同步下面先举一个代码示例:创建两个优先级相同的任务,这两个任务同时访问一个串口资源:void Task1Function(void * param){ int i; while (1) { printf("Task1\r\n"); }}void Task2Function(void * param){ whil...

@TOC


前言

本篇文章来为大家讲解信号量的具体使用。

一、使用二值信号量完成同步

下面先举一个代码示例:

创建两个优先级相同的任务,这两个任务同时访问一个串口资源:

void Task1Function(void * param)
{
	int i;
	while (1)
	{
		printf("Task1\r\n");
		
	}
}

void Task2Function(void * param)
{
	while (1)
	{
		printf("Task2\r\n");
		
	}
}

xTaskCreate(Task1Function, "Task1", 100, NULL, 1, NULL);
xTaskCreate(Task2Function, "Task2", 100, NULL, 1, NULL);

运行结果:

通过运行结果可以看出两个任务的打印信息交错在了一起,这就是同时访问共享资源带来的问题,这里我们可以使用二值信号量来解决这个问题。

在这里插入图片描述

void Task1Function(void * param)
{
	while (1)
	{
		if (xSemaphoreTake(xSem, portMAX_DELAY) == pdTRUE)
		{
			printf("Task1\r\n");
			xSemaphoreGive(xSem);
			vTaskDelay(1);
		}
		else
		{
			printf("Task1 xSemaphoreTake is err\r\n"); 
		}
	}
}

void Task2Function(void * param)
{
	while (1)
	{
		if (xSemaphoreTake(xSem, portMAX_DELAY) == pdTRUE)
		{
			printf("Task2\r\n");
			xSemaphoreGive(xSem);
			vTaskDelay(1);
		}
		else
		{
			printf("Task2 xSemaphoreTake is err\r\n"); 
		}
	}
}

xSem = xSemaphoreCreateBinary();
xSemaphoreGive(xSem);

xTaskCreate(Task1Function, "Task1", 100, NULL, 1, NULL);
xTaskCreate(Task2Function, "Task2", 100, NULL, 1, NULL);

运行效果:

通过现象可以得知,使用信号量可以完成同步访问共享资源。

在这里插入图片描述

二、使用计数型信号量

#define BUFFER_SIZE 5
#define NUM_PRODUCERS 2
#define NUM_CONSUMERS 2

SemaphoreHandle_t bufferMutex;
SemaphoreHandle_t itemsCount;

int buffer[BUFFER_SIZE];
int itemCount = 0;

void producerTask(void *param) {
    int producerId = (int)param;

    while (1) {
        // 产生一个随机的数据
        int data = rand() % 100;

        // 尝试获取 itemsCount 计数型信号量,表示可用的缓冲区数量
        xSemaphoreTake(itemsCount, portMAX_DELAY);

        // 获取 bufferMutex 二值型信号量,保护缓冲区的访问
        xSemaphoreTake(bufferMutex, portMAX_DELAY);

        // 将数据放入缓冲区
        buffer[itemCount] = data;
        itemCount++;

        printf("Producer %d - Produced: %d, Total items: %d\n", producerId, data, itemCount);

        // 释放 bufferMutex 二值型信号量
        xSemaphoreGive(bufferMutex);

        // 通知消费者有新的数据可用
        xSemaphoreGive(itemsCount);

        // 延时一段时间
        vTaskDelay(1000 / portTICK_PERIOD_MS);
    }
}

void consumerTask(void *param) {
    int consumerId = (int)param;

    while (1) {
        // 尝试获取 itemsCount 计数型信号量,表示可用的缓冲区数量
        xSemaphoreTake(itemsCount, portMAX_DELAY);

        // 获取 bufferMutex 二值型信号量,保护缓冲区的访问
        xSemaphoreTake(bufferMutex, portMAX_DELAY);

        // 从缓冲区获取数据
        int data = buffer[itemCount - 1];
        itemCount--;

        printf("Consumer %d - Consumed: %d, Total items: %d\n", consumerId, data, itemCount);

        // 释放 bufferMutex 二值型信号量
        xSemaphoreGive(bufferMutex);

        // 通知生产者有一个额外的缓冲区可用
        xSemaphoreGive(itemsCount);

        // 延时一段时间
        vTaskDelay(2000 / portTICK_PERIOD_MS);
    }
}

int main() {
    // 创建 bufferMutex 二值型信号量
    bufferMutex = xSemaphoreCreateMutex();

    // 创建 itemsCount 计数型信号量,初始值为 BUFFER_SIZE
    itemsCount = xSemaphoreCreateCounting(BUFFER_SIZE, BUFFER_SIZE);

    // 创建生产者任务
    for (int i = 0; i < NUM_PRODUCERS; i++) {
        xTaskCreate(producerTask, "Producer", configMINIMAL_STACK_SIZE, (void *)i, tskIDLE_PRIORITY + 1, NULL);
    }

    // 创建消费者任务
    for (int i = 0; i < NUM_CONSUMERS; i++) {
        xTaskCreate(consumerTask, "Consumer", configMINIMAL_STACK_SIZE, (void *)i, tskIDLE_PRIORITY + 2, NULL);
    }

    // 启动调度器
    vTaskStartScheduler();

    // 如果一切正常,下面的代码不应该执行到

    while (1) {
    }

    return 0;
}

使用二值型信号量 bufferMutex 来保护对缓冲区的访问,以防止多个任务同时访问引发竞争条件。使用计数型信号量 itemsCount 表示可用的缓冲区数量。当生产者将数据放入缓冲区时,会获取 itemsCount 信号量,并在获取成功后释放之前的信号量,从而告知消费者有新的数据可用。相反地,当消费者从缓冲区中取出数据时,会获取 itemsCount 信号量,并在获取成功后释放之前的信号量,从而告知生产者有一个额外的缓冲区可用。

总结

本篇文章就讲解到这里,大家多做实验多巩固复习。

【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。