互斥锁的使用

本文探讨了在多线程环境下,临界资源争抢可能导致的数据丢失和逻辑混乱问题,介绍了线程安全的概念,以及如何通过互斥锁和条件变量来实现线程间的互斥与同步,确保数据的安全访问。

 多个线程临界资源争抢写入操作会造成逻辑混乱/数据丢失二义性,因此就引入线程安全概念。

    线程使用本身就是线程间通信方便成本低而广为使用。这样的话就无法避免资源争抢操作,这时候就必须考虑线程安全。

    保证线程安全更多指的是保证数据的安全访问,保证数据访问的原子性不会被打断(互斥/同步)

线程间如何实现互斥与同步:

    互斥:互斥锁---保证数据的同一时间唯一访问

    同步:条件变量---让数据的访问更具有时间的可控性

/*  这是一个黄牛抢票的demo,互斥锁的使用
 */

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <pthread.h>

volatile ticket = 100;
pthread_mutex_t mutex;

void *yellow_cow(void *arg)
{
    int id = (int)arg;
    while(1) {
        //int pthread_mutex_lock(pthread_mutex_t *mutex);
        //int pthread_mutex_trylock(pthread_mutex_t *mutex);
        //int pthread_mutex_unlock(pthread_mutex_t *mutex);
        //lock 阻塞加锁,trylock 非阻塞加锁,timedlock 限时阻塞加锁
        pthread_mutex_lock(&mutex);
        if (ticket > 0) {
            usleep(100);
            ticket--;
            printf("cow %d get ticket:%d\n", id, ticket);
        }else {
            //在任何有可能退出的地方都必须在退出前解锁
            pthread_mutex_unlock(&mutex);
            pthread_exit(NULL);
        }
        pthread_mutex_unlock(&mutex);
    }
    return NULL;
}
int main()
{
    int max = 4, i;
    pthread_t tid[4];

    //  互斥锁变量的初始化
    //int pthread_mutex_init(pthread_mutex_t *restrict mutex,
    //  const pthread_mutexattr_t *restrict attr);
    //  这种初始化,在使用结束后必须要释放
    //  mutex:  互斥锁变量
    //  attr:  属性,通常置NULL
    //pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
    pthread_mutex_init(&mutex, NULL);
    for (i = 0; i < max; i++) {
        int ret = pthread_create(&tid[i],NULL,yellow_cow,(void*)i);
        if (ret != 0) {
            printf("create thread error\n");
            return -1;
        }
    }
    for (i = 0; i < max; i++) {
        pthread_join(tid[i], NULL);
    }
    //int pthread_mutex_destroy(pthread_mutex_t *mutex);
    //  销毁互斥锁   
pthread_mutex_destroy(&mutex);

    return 0;
}

 

<think> 首先,用户要求我查找FreeRTOS互斥锁使用实例。系统级指令要求我使用中文回答,并遵循特定的格式:行内数学表达式用$...$,独立公式用$$...$$,确保LaTeX语法正确。还需要生成相关问题,并在引用段落末尾添加引用标识。 用户提供的引用包括: - 引用[1]解释了互斥锁的创建,包括动态和静态创建方法。 - 引用[2]讨论了互斥量的函数,如创建、删除、give和take,并指出FreeRTOS中互斥锁的一个奇怪点:任务B可以释放任务A获得的锁。 - 引用[3]描述了一个使用实例:创建全局变量作为共享资源,任务1和任务2使用互斥锁来保护它。 用户的上一次问题是关于FreeRTOS互斥锁使用实例的,所以我需要基于这些引用构建一个清晰的实例。 我的回答结构应该: 1. 清晰逐步地解决问题。 2. 使用中文。 3. 在引用处添加引用标识[^n]。 内容计划: - 介绍互斥锁的作用:保护共享资源,避免竞争条件。 - 提供一个代码实例:基于引用[3],描述一个场景,任务1和任务2使用互斥锁访问共享变量。 - 解释关键函数:使用xSemaphoreCreateMutex()创建锁,xSemaphoreTake()获取锁,xSemaphoreGive()释放锁。 - 确保覆盖引用中的点,比如互斥锁的创建方式(动态vs静态),以及谁可以释放锁的特性。 实例描述: - 共享资源:全局变量如test_value。 - 任务1:每100ms获取锁,清空test_value,等待1秒检查是否被改写。 - 任务2:每10ms获取锁,设置test_value为1。 - 使用互斥锁确保在任务持有锁时,其他任务不能访问资源。 添加引用标识: - 在描述实例结束时引用引用[3]。 - 在讨论创建函数时引用引用[1]和[^2]。 最后,生成相关问题: - 例如:如何优化互斥锁使用互斥锁的死锁问题如何避免?互斥锁在不同场景中的应用。 完整的回答大纲: 1. 介绍互斥锁在FreeRTOS中的目的。 2. 提供详细的代码实例步骤: - 创建互斥锁。 - 定义任务函数。 - 任务中使用take和give。 3. 解释代码逻辑。 4. 添加引用。 5. 生成相关问题。 确保语言中文,专业但易于理解。 </think> ### FreeRTOS互斥锁使用实例 在FreeRTOS中,互斥锁(Mutex)用于保护共享资源,防止多个任务同时访问导致竞争条件(race condition)。下面我将基于引用内容,逐步构建一个清晰的使用实例。实例基于引用[3]的场景:创建一个全局变量作为共享资源,两个任务通过互斥锁协调访问。互斥锁的创建和操作函数参考引用[1]和引用[2]。 #### 实例场景描述 - **共享资源**: 定义一个全局变量 `test_value`(例如,作为计数器或状态标志)。 - **任务1**: 每100毫秒尝试获取互斥锁。成功后: - 将 `test_value` 清0。 - 等待1秒(模拟操作),期间检查 `test_value` 是否被其他任务修改。 - 如果未被修改,打印"测试成功";否则打印"测试失败"。 - 释放互斥锁。 - **任务2**: 每10毫秒尝试获取互斥锁。成功后: - 将 `test_value` 设置为1。 - 释放互斥锁。 - **互斥锁作用**: 确保当一个任务持有锁时,其他任务无法访问 `test_value`,防止数据不一致。 #### 代码实现步骤 以下是完整的FreeRTOS代码实例(使用C语言编写),假设已经配置好FreeRTOS环境(如 `FreeRTOSConfig.h` 中设置 `configUSE_MUTEXES=1`)。 1. **定义共享资源和互斥锁句柄**: ```c #include "FreeRTOS.h" #include "semphr.h" #include "task.h" // 定义共享资源(全局变量) volatile int test_value = 0; // 创建互斥锁句柄(动态创建) SemaphoreHandle_t xMutex; // 任务函数原型 void vTask1(void *pvParameters); void vTask2(void *pvParameters); ``` 2. **初始化互斥锁**: - 在 `main()` 函数中动态创建互斥锁。参考引用[1],互斥锁可以通过 `xSemaphoreCreateMutex()` 创建(动态分配RAM)。 - 如果静态创建,需使用 `xSemaphoreCreateMutexStatic()`(需设置 `configSUPPORT_STATIC_ALLOCATION=1`),但本示例使用动态方式。 ```c int main(void) { // 创建互斥锁(动态分配) xMutex = xSemaphoreCreateMutex(); if (xMutex == NULL) { // 处理创建失败错误 while(1); } // 创建任务 xTaskCreate(vTask1, "Task1", configMINIMAL_STACK_SIZE, NULL, tskIDLE_PRIORITY + 2, NULL); xTaskCreate(vTask2, "Task2", configMINIMAL_STACK_SIZE, NULL, tskIDLE_PRIORITY + 1, NULL); // 启动FreeRTOS调度器 vTaskStartScheduler(); while(1); // 调度器启动后,不应执行到这里 return 0; } ``` 3. **实现任务函数**: - **任务1**: 每100ms获取锁,操作资源并验证一致性。 ```c void vTask1(void *pvParameters) { TickType_t xLastWakeTime = xTaskGetTickCount(); const TickType_t xDelay = pdMS_TO_TICKS(100); // 100ms周期 for(;;) { // 尝试获取互斥锁,等待最多10个tick(portMAX_DELAY表示无限等待) if (xSemaphoreTake(xMutex, portMAX_DELAY) == pdTRUE) { test_value = 0; // 清空资源 // 模拟操作:等待1秒(1000ms),检查期间资源是否被修改 TickType_t xStartTime = xTaskGetTickCount(); while ((xTaskGetTickCount() - xStartTime) < pdMS_TO_TICKS(1000)) { if (test_value != 0) { // 如果被修改,打印失败 printf("测试失败: test_value 被意外修改!\n"); break; } } if (test_value == 0) { printf("测试成功: test_value 未被修改!\n"); } // 释放互斥锁 xSemaphoreGive(xMutex); } // 延迟100ms,等待下一个周期 vTaskDelayUntil(&xLastWakeTime, xDelay); } } ``` - **任务2**: 每10ms获取锁,修改资源。 ```c void vTask2(void *pvParameters) { TickType_t xLastWakeTime = xTaskGetTickCount(); const TickType_t xDelay = pdMS_TO_TICKS(10); // 10ms周期 for(;;) { // 获取互斥锁,无等待(如果锁被占用,立即返回) if (xSemaphoreTake(xMutex, 0) == pdTRUE) { test_value = 1; // 修改资源 xSemaphoreGive(xMutex); // 释放锁 } // 延迟10ms,等待下一个周期 vTaskDelayUntil(&xLastWakeTime, xDelay); } } ``` #### 关键点解释 - **互斥锁操作**: - `xSemaphoreTake()`: 获取锁。参数 `xTicksToWait` 指定等待时间(如 `portMAX_DELAY` 表示无限等待)。 - `xSemaphoreGive()`: 释放锁。注意 FreeRTOS 允许任意任务释放互斥锁(即使不是获取锁的任务),这可能导致意外行为(如任务B释放任务A的锁),因此在代码中需严格遵循"谁上锁谁释放"原则[^2]。 - **资源保护**: 通过互斥锁确保 `test_value` 的原子访问。当任务1持有锁时,任务2的 `xSemaphoreTake()` 会阻塞或失败(取决于等待设置),防止并发修改。 - **动态创建优势**: 本例使用 `xSemaphoreCreateMutex()` 简化RAM管理(从FreeRTOS堆自动分配)。如需静态创建,使用 `xSemaphoreCreateMutexStatic()` 并预分配 `StaticSemaphore_t` 缓冲区[^1]。 - **验证逻辑**: 任务1的等待模拟了实际场景中的资源检查,如果 `test_value` 在1秒内被任务2修改(表明互斥锁未生效),则打印错误消息。 #### 输出示例 在运行中,预期输出如下(取决于任务调度): - 大多数情况下,任务1获取锁后,任务2被阻塞,因此 `test_value` 保持0,打印"测试成功"。 - 如果互斥锁配置错误(如忘记释放),可能导致任务2意外修改值,打印"测试失败"。 此实例展示了互斥锁的基本应用,您可以扩展它处理更复杂共享资源(如数据结构或外设)[^3]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值