目录
互斥锁(Mutex)是一种同步机制,用于保护共享资源,确保同一时间只有一个任务可以访问该资源。互斥锁通过以下方式实现对共享资源的保护:
-
独占性:互斥锁确保在任何时刻,最多只有一个任务可以持有该锁。如果一个任务已经持有了互斥锁,其他试图获取该锁的任务会被阻塞,直到当前持有锁的任务释放它。
-
递归性:某些互斥锁支持递归锁定,即同一个任务可以多次获取同一个互斥锁而不会导致死锁。每次获取锁时,计数器会增加;每次释放锁时,计数器会减少。只有当计数器归零时,锁才会被完全释放,并允许其他任务获取。
-
优先级继承:互斥锁可以配置为支持优先级继承,以防止优先级反转问题。当高优先级的任务等待低优先级任务持有的互斥锁时,低优先级任务会暂时提升到高优先级任务的优先级,以确保高优先级任务能够尽快获得锁并继续执行。
一、互斥锁的工作原理
- 初始化:创建一个互斥锁,并设置其初始状态。
- 获取(Acquire):任务在访问共享资源之前,尝试获取互斥锁。如果互斥锁未被其他任务持有,则任务成功获取互斥锁;否则,任务会被阻塞,直到互斥锁被释放。
- 释放(Release):任务在完成对共享资源的访问后,释放互斥锁。如果有其他任务正在等待该互斥锁,那么其中一个任务会被唤醒并继续执行。
二、示例代码
以下是一个使用 CMSIS-RTOS v2 API 的示例,展示了如何使用互斥锁来保护共享资源:
C
深色版本
#include "cmsis_os2.h"
#include <stdio.h>
// 互斥锁属性
const osMutexAttr_t mutex_attr = {
.name = "MyMutex",
.attr_bits = osMutexRecursive | osMutexPrioInherit, // 可选属性
};
// 共享资源
int shared_resource = 0;
// 任务函数
void Task1(void *argument) {
osMutexId_t mutex_id = (osMutexId_t)argument;
while (1) {
// 获取互斥锁
osStatus_t status = osMutexAcquire(mutex_id, osWaitForever);
if (status == osOK) {
printf("Task 1: Acquired mutex\n");
// 访问共享资源
shared_resource++;
printf("Task 1: Shared resource value: %d\n", shared_resource);
// 模拟一些工作
for (int i = 0; i < 5; ++i) {
printf("Task 1: Working...\n");
osDelay(100);
}
// 释放互斥锁
status = osMutexRelease(mutex_id);
if (status == osOK) {
printf("Task 1: Released mutex\n");
} else {
// 处理释放互斥锁失败的情况
while (1);
}
} else {
// 处理获取互斥锁失败的情况
while (1);
}
// 延迟一段时间
osDelay(500);
}
}
void Task2(void *argument) {
osMutexId_t mutex_id = (osMutexId_t)argument;
while (1) {
// 获取互斥锁
osStatus_t status = osMutexAcquire(mutex_id, osWaitForever);
if (status == osOK) {
printf("Task 2: Acquired mutex\n");
// 访问共享资源
shared_resource--;
printf("Task 2: Shared resource value: %d\n", shared_resource);
// 模拟一些工作
for (int i = 0; i < 5; ++i) {
printf("Task 2: Working...\n");
osDelay(100);
}
// 释放互斥锁
status = osMutexRelease(mutex_id);
if (status == osOK) {
printf("Task 2: Released mutex\n");
} else {
// 处理释放互斥锁失败的情况
while (1);
}
} else {
// 处理获取互斥锁失败的情况
while (1);
}
// 延迟一段时间
osDelay(500);
}
}
int main(void) {
// 初始化 RTOS
osKernelInitialize();
// 创建互斥锁
osMutexId_t mutex_id = osMutexNew(&mutex_attr);
if (mutex_id == NULL) {
// 处理互斥锁创建失败的情况
while (1);
}
// 创建任务
const osThreadAttr_t thread_attr1 = {
.name = "Task1",
.stack_size = 512,
.priority = (osPriority_t)osPriorityNormal,
};
osThreadId_t thread_id1 = osThreadNew(Task1, (void *)mutex_id, &thread_attr1);
if (thread_id1 == NULL) {
// 处理任务创建失败的情况
while (1);
}
const osThreadAttr_t thread_attr2 = {
.name = "Task2",
.stack_size = 512,
.priority = (osPriority_t)osPriorityNormal,
};
osThreadId_t thread_id2 = osThreadNew(Task2, (void *)mutex_id, &thread_attr2);
if (thread_id2 == NULL) {
// 处理任务创建失败的情况
while (1);
}
// 启动 RTOS 调度器
osKernelStart();
// 不会到达这里
while (1);
}
三、解释
-
初始化:
- 在
main
函数中,我们创建了一个互斥锁mutex_id
,并设置了递归和优先级继承属性。
- 在
-
任务定义:
Task1
和Task2
是两个任务,它们都试图访问同一个共享资源shared_resource
。
-
获取互斥锁:
- 在每个任务中,首先调用
osMutexAcquire
来获取互斥锁。如果互斥锁未被其他任务持有,则任务成功获取互斥锁;否则,任务会被阻塞,直到互斥锁被释放。
- 在每个任务中,首先调用
-
访问共享资源:
- 一旦任务成功获取了互斥锁,就可以安全地访问共享资源。在这个示例中,
Task1
增加shared_resource
的值,而Task2
减少shared_resource
的值。
- 一旦任务成功获取了互斥锁,就可以安全地访问共享资源。在这个示例中,
-
释放互斥锁:
- 任务在完成对共享资源的访问后,调用
osMutexRelease
释放互斥锁。如果有其他任务正在等待该互斥锁,那么其中一个任务会被唤醒并继续执行。
- 任务在完成对共享资源的访问后,调用
通过这种方式,互斥锁确保了一次只有一个任务可以访问共享资源,从而避免了竞争条件。同时,递归锁定和优先级继承等特性提供了额外的灵活性和安全性。
四、运行结果
由于 Task1
和 Task2
是并发执行的,并且使用了互斥锁来保护共享资源 shared_resource
,因此可以确保在同一时间只有一个任务可以访问和修改 shared_resource
。以下是可能的运行结果示例:
深色版本
Task 1: Acquired mutex
Task 1: Shared resource value: 1
Task 1: Working...
Task 1: Working...
Task 1: Working...
Task 1: Working...
Task 1: Working...
Task 1: Released mutex
Task 2: Acquired mutex
Task 2: Shared resource value: 0
Task 2: Working...
Task 2: Working...
Task 2: Working...
Task 2: Working...
Task 2: Working...
Task 2: Released mutex
Task 1: Acquired mutex
Task 1: Shared resource value: 1
Task 1: Working...
Task 1: Working...
Task 1: Working...
Task 1: Working...
Task 1: Working...
Task 1: Released mutex
Task 2: Acquired mutex
Task 2: Shared resource value: 0
Task 2: Working...
Task 2: Working...
Task 2: Working...
Task 2: Working...
Task 2: Working...
Task 2: Released mutex
...
五、输出结果解释
- 互斥锁的作用:互斥锁确保
shared_resource
只能被一个任务在某一时刻访问。例如,当Task1
获取了互斥锁并增加shared_resource
的值时,Task2
必须等待Task1
释放互斥锁后才能获取互斥锁并减少shared_resource
的值。 - 交替执行:由于
Task1
和Task2
的优先级相同,且每次操作后都有 500 毫秒的延迟,所以它们会交替执行。每次Task1
增加shared_resource
的值,Task2
就会减少shared_resource
的值,从而保持shared_resource
的值在 0 和 1 之间变化。 - 输出顺序:具体的输出顺序可能会有所不同,因为任务调度是基于 RTOS 的调度策略,但总体上
Task1
和Task2
会交替执行,每个任务在获取和释放互斥锁时会有相应的输出。
六、注意事项
- 死锁预防:确保每个任务在完成对共享资源的操作后总是释放互斥锁,以避免死锁。
- 错误处理:在实际应用中,应该更好地处理获取和释放互斥锁失败的情况,而不是简单地进入无限循环。
通过这种方式,互斥锁有效地保护了共享资源,确保了数据的一致性和完整性。