RT-Thread 中避免多任务共享函数时的冲突问题

场景 1:共享全局变量(使用互斥锁)

问题​:多个任务同时修改全局计数器导致数据不一致
解决方案:使用互斥锁保护临界区

#include <rtthread.h>

static int counter = 0;
static struct rt_mutex counter_mutex;

void increment_counter(void) {
    rt_mutex_take(&counter_mutex, RT_WAITING_FOREVER); // 上锁
    counter++; // 安全操作
    rt_kprintf("Counter: %d\n", counter);
    rt_mutex_release(&counter_mutex); // 解锁
}

void thread_entry(void* param) {
    while (1) {
        increment_counter(); // 多个任务调用同一函数
        rt_thread_mdelay(50);
    }
}

int main(void) {
    rt_mutex_init(&counter_mutex, "cnt_mtx", RT_IPC_FLAG_FIFO);
    
    // 创建3个任务
    for (int i = 0; i < 3; i++) {
        rt_thread_t tid = rt_thread_create("t", thread_entry, RT_NULL, 512, 25, 10);
        rt_thread_startup(tid);
    }
    return 0;
}

场景 2:共享硬件资源(使用信号量)

问题​:多个任务同时操作串口导致输出混乱
解决方案​:使用二值信号量实现互斥访问

static rt_sem_t uart_sem;

void uart_print(const char* msg) {
    rt_sem_take(uart_sem, RT_WAITING_FOREVER); // 获取信号量
    rt_device_write(uart_dev, 0, msg, rt_strlen(msg)); // 安全操作
    rt_sem_release(uart_sem); // 释放信号量
}

void task1_entry(void* param) {
    while (1) {
        uart_print("Task1 sending data\n");
        rt_thread_mdelay(100);
    }
}

void task2_entry(void* param) {
    while (1) {
        uart_print("Task2 sending log\n");
        rt_thread_mdelay(150);
    }
}

void app_init() {
    // 创建二值信号量(初始值为1)
    uart_sem = rt_sem_create("uart_sem", 1, RT_IPC_FLAG_FIFO);
    
    // 初始化串口设备...
    uart_dev = rt_device_find("uart1");
    rt_device_open(uart_dev, RT_DEVICE_FLAG_RDWR);
}

场景 3:非线程安全库函数(使用线程局部存储)

问题​:多个任务同时使用 strtok 等非可重入函数
解决方案​:使用可重入版本或线程局部变量

#include <string.h>

// 线程安全的strtok版本
char* safe_strtok(char* str, const char* delim) {
    static RT_THREAD_LOCAL char* save_ptr; // 线程局部存储
    return strtok_r(str, delim, &save_ptr);
}

void parse_data(char* data) {
    char* token = safe_strtok(data, ",");
    while (token) {
        rt_kprintf("Token: %s\n", token);
        token = safe_strtok(NULL, ",");
    }
}

场景 4:复杂共享资源(使用消息队列)

问题​:多个任务需要访问复杂数据结构(如链表)
解决方案​:通过消息队列委托给专用处理线程

static rt_mq_t data_mq;

// 数据处理线程(唯一访问者)
void data_processor_entry(void* param) {
    struct data_packet packet;
    while (1) {
        if (rt_mq_recv(data_mq, &packet, sizeof(packet), RT_WAITING_FOREVER) > 0) {
            // 安全处理共享数据
            process_data(&packet);
        }
    }
}

// 其他任务通过消息队列提交请求
void submit_data_request(int type, void* data) {
    struct data_packet packet = {type, data};
    rt_mq_send(data_mq, &packet, sizeof(packet));
}

void worker_task_entry(void* param) {
    while (1) {
        void* data = collect_data();
        submit_data_request(DATA_PROCESS, data); // 不直接操作共享资源
        rt_thread_mdelay(200);
    }
}

场景 5:读多写少场景(使用读写锁)

问题​:配置数据被频繁读取但偶尔修改
解决方案​:使用读写锁提高并发性

static rt_rwlock_t config_rwlock;
static struct app_config global_config;

// 读取配置(多个任务可同时读)
void get_config(struct app_config* out) {
    rt_rwlock_rdlock(&config_rwlock);
    memcpy(out, &global_config, sizeof(struct app_config));
    rt_rwlock_rdunlock(&config_rwlock);
}

// 更新配置(独占访问)
void update_config(const struct app_config* new_cfg) {
    rt_rwlock_wrlock(&config_rwlock);
    memcpy(&global_config, new_cfg, sizeof(struct app_config));
    rt_rwlock_wrunlock(&config_rwlock);
}

最佳实践总结:

  1. 最小化共享​:尽量设计无状态函数,使用局部变量
  2. 锁粒度控制​:
    // 错误示范:锁范围过大
    rt_mutex_take(&lock);
    complex_operation1();
    complex_operation2(); // 阻塞其他任务过久
    rt_mutex_release(&lock);
    
    // 正确做法:只保护真正共享的部分
    complex_operation1(); // 非临界区操作
    rt_mutex_take(&lock);
    critical_section();   // 快速完成
    rt_mutex_release(&lock);
    complex_operation2();
  3. 避免锁嵌套​:防止死锁
  4. 优先使用消息队列​:减少直接共享
  5. 原子操作​:简单计数使用原子变量
    #include <stdatomic.h>
    atomic_int safe_counter = ATOMIC_VAR_INIT(0);
    
    void increment_atomic() {
        atomic_fetch_add(&safe_counter, 1);
    }

通过合理选择同步机制,可以确保多任务安全共享函数,同时保持系统效率。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值