目录
1. 互斥(Mutex)
互斥是一种机制,用于确保在任何给定时间只有一个任务能够访问共享资源。当一个任务获得了互斥锁时,在它释放互斥锁之前,其他任务将无法获得同一个互斥锁。这样可以有效地避免多个任务同时访问和修改共享资源导致的竞态条件或数据不一致问题。
在FreeRTOS中,使用xSemaphoreCreateMutex()函数创建一个互斥锁(Mutex Semaphore)。通过调用xSemaphoreTake()获取互斥锁,调用xSemaphoreGive()释放互斥锁。
2. 同步(Synchronization)
同步是一种机制,用于确保多个任务能够按照特定的顺序协调执行或共享数据。当一个任务需要等待其他任务完成某个操作或满足某个条件时,同步机制可以帮助任务进行协调和等待。
在FreeRTOS中,常见的同步机制包括任务通知(Task Notification)、信号量(Semaphore)、事件标志组(Event Group)等。这些机制可以通过任务等待和任务唤醒来实现任务之间的协调和数据交换。
- 任务通知:任务通知允许任务之间通信和同步状态。任务可以等待特定的通知事件,并在事件发生时被唤醒。任务之间可以通过任务通知来传递数据和信息。
- 信号量:信号量是一种计数器,用于控制对资源的访问。当一个任务需要访问共享资源时,它可以尝试获取一个信号量。如果信号量计数器为正(表示有可用资源),则任务可以继续执行。如果计数器为零,则任务进入阻塞状态,等待信号量计数器变为正。
- 事件标志组:事件标志组是一种用于任务通信和同步的机制,可以通过设置、清除和等待事件标志来实现任务间的同步和数据传递。任务可以等待多个特定的事件标志同时发生,以及循环等待、超时等。
上述同步机制都可以使用相应的FreeRTOS API函数来创建、等待和释放。
所以,互斥用于保护共享资源的独占访问,同步用于任务之间的协调和数据交换。通过适当的使用互斥和同步机制,可以确保任务之间的安全访问和正确的执行顺序。
通俗易懂的例子加代码
考虑一个模拟银行账户的场景,有两个任务:`Task_A`和`Task_B`,它们同时访问共享的银行账户`balance`。
volatile int balance = 0;
假设`Task_A`是一个存款任务,`Task_B`是一个取款任务。现在我们来看看如何使用互斥和同步机制来保护共享资源。
1. 互斥(Mutex):
我们可以使用一个互斥锁来确保在任何给定时间只有一个任务能够修改账户余额。当一个任务在存款或取款前获得互斥锁时,其他任务将无法获得同一个互斥锁,从而保证了操作的原子性。
SemaphoreHandle_t mutex = NULL; // 互斥锁
// 在任务初始化中创建互斥锁
void init_tasks(void) {
mutex = xSemaphoreCreateMutex();
}
// Task_A 存款任务
void Task_A(void *pvParameters) {
while(1) {
// 获取互斥锁
xSemaphoreTake(mutex, portMAX_DELAY);
// 存款操作
balance += 100;
// 释放互斥锁
xSemaphoreGive(mutex);
}
}
// Task_B 取款任务
void Task_B(void *pvParameters) {
while(1) {
// 获取互斥锁
xSemaphoreTake(mutex, portMAX_DELAY);
// 取款操作
balance -= 50;
// 释放互斥锁
xSemaphoreGive(mutex);
}
}
在上述示例中,当一个任务执行存款或取款操作时,它将获取互斥锁并在完成操作后释放互斥锁,这样在同一时间只有一个任务能够修改账户余额。
2. 同步(Synchronization):
我们可以使用一个信号量来实现任务之间的同步,确保取款任务在账户余额不足时等待存款任务补充余额。
SemaphoreHandle_t semaphore = NULL; // 信号量
// 在任务初始化中创建信号量
void init_tasks(void) {
semaphore = xSemaphoreCreateBinary();
}
// Task_A 存款任务
void Task_A(void *pvParameters) {
while(1) {
// 存款操作
balance += 100;
// 发送信号,通知取款任务可以继续执行
xSemaphoreGive(semaphore);
}
}
// Task_B 取款任务
void Task_B(void *pvParameters) {
while(1) {
// 等待信号,如果账户余额不足就阻塞等待
xSemaphoreTake(semaphore, portMAX_DELAY);
// 取款操作
balance -= 50;
}
}
在上述示例中,取款任务等待信号量变为可用(即存款任务将金额存入账户),这样可以确保取款任务在有足够余额时才能执行。
通过在存款和取款任务中使用互斥锁和信号量,我们实现了对共享资源的互斥访问和任务之间的同步,确保操作的正确性和数据的一致性。