• 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);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    运行结果:

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

    在这里插入图片描述

    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);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39

    运行效果:

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

    在这里插入图片描述

    二、使用计数型信号量

    #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;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94

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

    总结

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

  • 相关阅读:
    Idea+springboot+vue 前后端交互实现
    Tomcat使用教程(超详细)
    归并排序-Python
    阿里云X魔搭社区Create@AI创客松第四届冠军:MumuLab
    网红探店一般怎么收费?找网红探店该如何筛选博主
    MyBatis 如何进行一对一关联查询呢?
    【Web】记录CISCN 2023 西南半决赛 seaclouds题目复现
    .Net 5.0快速上手 Redis
    从网页的canvas上保存渲染的图片
    云计算采用的各种虚拟化技术比较
  • 原文地址:https://blog.csdn.net/m0_49476241/article/details/133622301