五、信号量、互斥锁
FreeRTOS 提供了信号量和互斥锁,用于任务间的同步和资源共享管理。信号量更偏向于任务同步,而互斥锁用于保护共享资源。
5.1 二进制信号量
二进制信号量是最基本的信号量,仅有两个状态:可用和不可用(或 1 和 0)。 通常用于任务之间或中断与任务之间的同步,当一个事件发生时,由中断或任务释放信号量,等待信号量的任务就会被唤醒。 二进制信号量适用于简单的事件通知场景,比如通知某个任务处理外部输入或完成某项任务。
5.1.1 API说明:
5.1.2 示例代码:
c
// 二进制信号量
#include <stdio.h>
#include "esp_log.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/semphr.h"
static const char *TAG = "main";
// 二进制信号量
SemaphoreHandle_t semaphoreHandle;
// 公共变量
int shareVariable = 0;
void task1(void *pvParameters)
{
for (;;)
{
// 获取信号量,信号量变为0
xSemaphoreTake(semaphoreHandle, portMAX_DELAY);
for (int i = 0; i < 10; i++)
{
shareVariable++;
ESP_LOGI(TAG, "task1 shareVariable:%d", shareVariable);
vTaskDelay(pdMS_TO_TICKS(1000));
}
// 释放信号量,信号量变为1
xSemaphoreGive(semaphoreHandle);
vTaskDelay(pdMS_TO_TICKS(1000));
}
}
void task2(void *pvParameters)
{
for (;;)
{
// 获取信号量
xSemaphoreTake(semaphoreHandle, portMAX_DELAY);
for (int i = 0; i < 10; i++)
{
shareVariable++;
ESP_LOGI(TAG, "task2 shareVariable:%d", shareVariable);
vTaskDelay(pdMS_TO_TICKS(1000));
}
// 释放信号量,信号量变为1
xSemaphoreGive(semaphoreHandle);
vTaskDelay(pdMS_TO_TICKS(1000));
}
}
void app_main(void)
{
semaphoreHandle = xSemaphoreCreateBinary();
xSemaphoreGive(semaphoreHandle);
// 创建任务
xTaskCreate(task1, "task1", 1024 * 2, NULL, 10, NULL);
xTaskCreate(task2, "task2", 1024 * 2, NULL, 10, NULL);
}
5.2 计数信号量
5.2.2 示例代码:
c
// 计数型信号量(占座)
#include <stdio.h>
#include "esp_log.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/semphr.h"
static const char *TAG = "main";
// 信号量
SemaphoreHandle_t semaphoreHandle;
// 占座任务
void task1(void *pvParameters)
{
// 定义空位
int seat = 0;
for (;;)
{
// 获取信号量
seat = uxSemaphoreGetCount(semaphoreHandle);
// 输出空位
ESP_LOGI(TAG, "当前空位:%d", seat);
// 获取信号量(占座)
if (xSemaphoreTake(semaphoreHandle, portMAX_DELAY) == pdPASS)
{
ESP_LOGI(TAG, "占座成功");
}
else
{
ESP_LOGI(TAG, "占座失败");
}
vTaskDelay(pdMS_TO_TICKS(1000));
}
}
// 离开座位任务
void task2(void *pvParameters)
{
for (;;)
{
vTaskDelay(pdMS_TO_TICKS(6000));
// 释放信号量
xSemaphoreGive(semaphoreHandle);
ESP_LOGI(TAG, "释放座位");
}
}
void app_main(void)
{
semaphoreHandle = xSemaphoreCreateCounting(5, 5);
// 创建占座任务
xTaskCreate(task1, "task1", 1024 * 2, NULL, 10, NULL);
// 创建离开座位任务
xTaskCreate(task2, "task2", 1024 * 2, NULL, 10, NULL);
}
5.3 互斥量
互斥量(互斥锁Mutex):互斥锁和二进制信号量极为相似,但 有一些细微差异:互斥锁具有优先级继承机制, 但二进制信号量没有。
5.3.2 示例代码:
c
#include <stdio.h>
#include "esp_log.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/semphr.h"
static const char *TAG = "main";
SemaphoreHandle_t mutexHandle;
void task1(void *pvParameters)
{
ESP_LOGI(TAG, "task1启动!");
while (1)
{
if (xSemaphoreTake(mutexHandle, portMAX_DELAY) == pdTRUE)
{
ESP_LOGI(TAG, "task1获取到互斥量!");
for (int i = 0; i < 10; i++)
{
ESP_LOGI(TAG, "task1执行中...%d", i);
vTaskDelay(pdMS_TO_TICKS(1000));
}
xSemaphoreGive(mutexHandle);
ESP_LOGI(TAG, "task1释放互斥量!");
vTaskDelay(pdMS_TO_TICKS(1000));
}
else
{
ESP_LOGI(TAG, "task1获取互斥量失败!");
vTaskDelay(pdMS_TO_TICKS(1000));
}
}
}
void task2(void *pvParameters)
{
ESP_LOGI(TAG, "task2启动!");
vTaskDelay(pdMS_TO_TICKS(1000));
while (1)
{
}
}
void task3(void *pvParameters)
{
ESP_LOGI(TAG, "task3启动!");
vTaskDelay(pdMS_TO_TICKS(1000));
while (1)
{
if (xSemaphoreTake(mutexHandle, 1000) == pdPASS)
{
ESP_LOGI(TAG, "task3获取到互斥量!");
for (int i = 0; i < 10; i++)
{
ESP_LOGI(TAG, "task3执行中...%d",i);
vTaskDelay(pdMS_TO_TICKS(1000));
}
xSemaphoreGive(mutexHandle);
ESP_LOGI(TAG, "task3释放互斥量!");
vTaskDelay(pdMS_TO_TICKS(5000));
}
else
{
ESP_LOGI(TAG, "task3未获取到互斥量!");
vTaskDelay(pdMS_TO_TICKS(1000));
}
}
}
void app_main(void)
{
mutexHandle = xSemaphoreCreateMutex();
// mutexHandle = xSemaphoreCreateBinary();
xTaskCreate(task1, "task1", 1024 * 2, NULL, 1, NULL);
xTaskCreate(task2, "task2", 1024 * 2, NULL, 2, NULL);
xTaskCreate(task3, "task3", 1024 * 2, NULL, 3, NULL);
}
5.4 递归互斥量
非递归互斥锁只能被一个任务 获取一次,如果同一个任务想再次获取则会失败, 因为当任务第一次释放互斥锁时,互斥锁就一直处于释放状态。与非递归互斥锁相反,递归互斥锁可以被同一个任务获取很多次, 获取多少次就需要释放多少次, 此时才会返回递归互斥锁。
5.5 示例代码:
c
#include <stdio.h>
#include "esp_log.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/semphr.h"
static const char *TAG = "main";
SemaphoreHandle_t mutexHandle;
void task1(void *pvParameters)
{
ESP_LOGI(TAG, "-------------------------------");
ESP_LOGI(TAG, "task1启动!");
while (1)
{
xSemaphoreTakeRecursive(mutexHandle, portMAX_DELAY);
ESP_LOGI(TAG, "task1获取到互斥量-使用资源A");
for (int i = 0; i < 10; i++)
{
ESP_LOGI(TAG, "task1执行中...%d -使用资源A", i);
vTaskDelay(pdMS_TO_TICKS(1000));
}
xSemaphoreTakeRecursive(mutexHandle, portMAX_DELAY);
ESP_LOGI(TAG, "task1获取到互斥量-使用资源B");
for (int i = 0; i < 10; i++)
{
ESP_LOGI(TAG, "task1执行中...%d -使用资源B", i);
vTaskDelay(pdMS_TO_TICKS(1000));
}
xSemaphoreGiveRecursive(mutexHandle);
ESP_LOGI(TAG, "task1释放互斥量-使用资源B");
vTaskDelay(pdMS_TO_TICKS(3000));
xSemaphoreGiveRecursive(mutexHandle);
ESP_LOGI(TAG, "task1释放互斥量-使用资源A");
}
vTaskDelete(NULL);
}
void task2(void *pvParameters)
{
ESP_LOGI(TAG, "task2启动!");
vTaskDelay(pdMS_TO_TICKS(1000));
while (1)
{
// 获取递归互斥锁
if (xSemaphoreTakeRecursive(mutexHandle, portMAX_DELAY) == pdTRUE)
{
ESP_LOGI(TAG, "task2获取到互斥量");
for (int i = 0; i < 10; i++)
{
ESP_LOGI(TAG, "task2执行中...%d", i);
vTaskDelay(pdMS_TO_TICKS(1000));
}
xSemaphoreGiveRecursive(mutexHandle);
ESP_LOGI(TAG, "task2释放互斥量");
}
else
{
ESP_LOGI(TAG, "task2获取互斥量失败");
vTaskDelay(pdMS_TO_TICKS(1000));
}
}
}
void app_main(void)
{
// 创建递归互斥量
mutexHandle = xSemaphoreCreateRecursiveMutex();
xTaskCreate(task1, "task1", 1024 * 2, NULL, 1, NULL);
xTaskCreate(task2, "task2", 1024 * 2, NULL, 2, NULL);
}