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


前言

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

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

下面先举一个代码示例:

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

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 信号量,并在获取成功后释放之前的信号量,从而告知生产者有一个额外的缓冲区可用。

总结

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

猜你喜欢

转载自blog.csdn.net/m0_49476241/article/details/133622301
今日推荐