news 2026/3/11 2:44:28

35.记录数据共享问题

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
35.记录数据共享问题

1.互斥量共享内存的例子

一个使用互斥量(Mutex)实现两个线程共享内存的示例,具体场景是一个线程模拟读取传感器数据并写入数组,另一个线程从数组读取数据并处理(显示 / 存储),核心是通过互斥量保证数据访问的线程安全。

实现思路

  1. 共享资源定义:定义一个数组作为传感器数据缓冲区,同时定义一个互斥量保护这个数组的访问。
  2. 线程 1(生产者):模拟传感器数据采集,每次生成随机数据后,先加锁互斥量,写入数组,解锁后休眠一段时间(模拟采样间隔)。
  3. 线程 2(消费者):循环读取数组数据,先加锁互斥量,读取并处理数据(打印显示),解锁后休眠一段时间(模拟处理间隔)。
  4. 主线程:创建并启动两个子线程,等待线程结束后释放资源。
#include <stdio.h> #include <stdlib.h> #include <pthread.h> #include <unistd.h> #include <time.h> // 定义共享数据和互斥量 #define BUFFER_SIZE 5 // 传感器数据缓冲区大小 int sensor_data[BUFFER_SIZE]; // 共享数组(传感器数据缓冲区) pthread_mutex_t data_mutex; // 保护共享数组的互斥量 // 传感器数据生成函数(模拟读取真实传感器数据) int generate_sensor_data() { // 生成0-99的随机数模拟传感器数据 return rand() % 100; } // 线程1:生产者 - 读取传感器数据并写入数组 void* sensor_reader_thread(void* arg) { (void)arg; // 忽略未使用的参数 int index = 0; // 数组写入索引 while (1) { // 1. 生成模拟传感器数据 int new_data = generate_sensor_data(); // 2. 加锁互斥量,保证独占访问共享数组 pthread_mutex_lock(&data_mutex); // 3. 写入共享数组(循环覆盖) sensor_data[index] = new_data; printf("[生产者线程] 写入数据: %d 到数组索引 %d\n", new_data, index); index = (index + 1) % BUFFER_SIZE; // 循环索引,满了从头覆盖 // 4. 解锁互斥量,释放共享资源 pthread_mutex_unlock(&data_mutex); // 模拟传感器采样间隔(1秒采集一次) sleep(1); } return NULL; } // 线程2:消费者 - 从数组读取数据并处理(打印/存储) void* data_processor_thread(void* arg) { (void)arg; // 忽略未使用的参数 int index = 0; // 数组读取索引 while (1) { // 1. 加锁互斥量,保证独占访问共享数组 pthread_mutex_lock(&data_mutex); // 2. 读取共享数组数据并处理 int read_data = sensor_data[index]; printf("[消费者线程] 读取数据: %d 从数组索引 %d\n", read_data, index); index = (index + 1) % BUFFER_SIZE; // 循环索引,同步读取位置 // 3. 解锁互斥量,释放共享资源 pthread_mutex_unlock(&data_mutex); // 模拟数据处理间隔(1秒处理一次) sleep(1); } return NULL; } int main() { // 初始化随机数种子(用于模拟传感器数据) srand((unsigned int)time(NULL)); // 初始化互斥量(必须初始化,否则会导致未定义行为) if (pthread_mutex_init(&data_mutex, NULL) != 0) { perror("互斥量初始化失败"); return 1; } // 初始化共享数组为0 for (int i = 0; i < BUFFER_SIZE; i++) { sensor_data[i] = 0; } // 定义线程ID pthread_t reader_thread, processor_thread; // 创建生产者线程(传感器数据读取) if (pthread_create(&reader_thread, NULL, sensor_reader_thread, NULL) != 0) { perror("创建生产者线程失败"); pthread_mutex_destroy(&data_mutex); // 清理互斥量 return 1; } // 创建消费者线程(数据处理) if (pthread_create(&processor_thread, NULL, data_processor_thread, NULL) != 0) { perror("创建消费者线程失败"); pthread_cancel(reader_thread); // 取消已创建的线程 pthread_mutex_destroy(&data_mutex); // 清理互斥量 return 1; } // 等待线程结束(这里用无限循环,实际场景可设置退出条件) pthread_join(reader_thread, NULL); pthread_join(processor_thread, NULL); // 销毁互斥量(释放资源) pthread_mutex_destroy(&data_mutex); return 0; }

2.全局变量的方式

#include <rtthread.h> #include <rtdevice.h> #include <board.h> /* -------------------------- 全局共享资源定义 -------------------------- */ #define SENSOR_BUFFER_SIZE 5 // 传感器数据缓冲区大小 static rt_int32_t sensor_buffer[SENSOR_BUFFER_SIZE] = {0}; // 共享数组 // 原子变量:写入索引(线程用)、读取索引(中断用)、数据有效标志 static rt_atomic_t write_idx = RT_ATOMIC_INIT(0); static rt_atomic_t read_idx = RT_ATOMIC_INIT(0); static rt_atomic_t buffer_valid[SENSOR_BUFFER_SIZE] = { RT_ATOMIC_INIT(0), RT_ATOMIC_INIT(0), RT_ATOMIC_INIT(0), RT_ATOMIC_INIT(0), RT_ATOMIC_INIT(0) }; /* -------------------------- 传感器数据模拟 -------------------------- */ // 模拟读取传感器数据(返回0-99随机数) static rt_int32_t sensor_read_data(void) { static rt_uint32_t seed = 12345; seed = seed * 1103515245 + 12345; return (rt_int32_t)((seed / 65536) % 100); } /* -------------------------- 线程:传感器数据采集(生产者) -------------------------- */ static void sensor_collect_thread(void *parameter) { rt_int32_t idx; rt_int32_t data; while (1) { // 1. 读取模拟传感器数据 data = sensor_read_data(); // 2. 获取当前写入索引(原子操作,避免多线程竞争) idx = rt_atomic_read(&write_idx); // 3. 安全写入共享数组(RT_ATOMIC_SECTION 保证操作原子性) RT_ATOMIC_SECTION_BEGIN sensor_buffer[idx] = data; // 写入数据 rt_atomic_set(&buffer_valid[idx], 1); // 标记该位置数据有效 RT_ATOMIC_SECTION_END // 4. 更新写入索引(循环覆盖) rt_atomic_set(&write_idx, (idx + 1) % SENSOR_BUFFER_SIZE); rt_kprintf("[采集线程] 写入数据: %d 到索引 %d\n", data, idx); // 模拟传感器采样间隔(500ms) rt_thread_mdelay(500); } } /* -------------------------- 中断服务程序(ISR):数据读取(消费者) -------------------------- */ // 定时器中断服务函数(模拟硬件中断,替代实际传感器中断) static rt_err_t timer_isr_hook(rt_device_t dev, rt_size_t size) { rt_int32_t idx; rt_int32_t data; // 中断上下文:禁止调用任何可能休眠的函数(如rt_thread_mdelay、rt_kprintf慎用) // 1. 获取当前读取索引(原子操作) idx = rt_atomic_read(&read_idx); // 2. 检查数据是否有效,有效则读取 if (rt_atomic_read(&buffer_valid[idx]) == 1) { // 原子操作读取数据,保证完整性 RT_ATOMIC_SECTION_BEGIN data = sensor_buffer[idx]; // 读取数据 rt_atomic_set(&buffer_valid[idx], 0); // 标记该位置数据已读取 RT_ATOMIC_SECTION_END // 中断中尽量简化操作,这里仅打印(实际场景可写入存储/寄存器) rt_kprintf("[中断ISR] 读取数据: %d 从索引 %d\n", data, idx); // 3. 更新读取索引(循环读取) rt_atomic_set(&read_idx, (idx + 1) % SENSOR_BUFFER_SIZE); } else { // 无有效数据时的提示(可选) // rt_kprintf("[中断ISR] 索引 %d 无有效数据\n", idx); } return RT_EOK; } /* -------------------------- 初始化函数 -------------------------- */ int sensor_interrupt_share_demo(void) { rt_thread_t collect_thread; rt_device_t timer_dev; struct rt_device_timer_info timer_info; // 1. 创建传感器采集线程 collect_thread = rt_thread_create( "sensor_collect", // 线程名称 sensor_collect_thread, // 线程入口函数 RT_NULL, // 线程参数 1024, // 栈大小 10, // 优先级(数值越小优先级越高) 20 // 时间片 ); if (collect_thread != RT_NULL) { rt_thread_startup(collect_thread); rt_kprintf("传感器采集线程创建成功\n"); } else { rt_kprintf("传感器采集线程创建失败\n"); return -RT_ERROR; } // 2. 初始化定时器设备(模拟硬件中断,使用RT-Thread系统定时器) timer_dev = rt_device_find("timer0"); if (timer_dev == RT_NULL) { rt_kprintf("定时器设备查找失败\n"); return -RT_ERROR; } // 3. 打开定时器设备 if (rt_device_open(timer_dev, RT_DEVICE_OFLAG_RDWR) != RT_EOK) { rt_kprintf("定时器设备打开失败\n"); return -RT_ERROR; } // 4. 设置定时器回调函数(中断服务程序) rt_device_set_rx_indicate(timer_dev, timer_isr_hook); // 5. 配置定时器参数(1秒触发一次中断) timer_info.period = 1000; // 周期(ms) timer_info.mode = RT_DEVICE_TIMER_PERIODIC; // 周期性触发 rt_device_control(timer_dev, RT_DEVICE_CTRL_TIMER_SET_PERIOD, &timer_info); // 6. 启动定时器 rt_device_control(timer_dev, RT_DEVICE_CTRL_TIMER_START, RT_NULL); rt_kprintf("定时器中断初始化成功,1秒触发一次\n"); return RT_EOK; } // 导出到RT-Thread的msh命令 MSH_CMD_EXPORT(sensor_interrupt_share_demo, sensor interrupt share memory demo);

3.避坑

避坑1:

互斥信号量不要在线程和中断之间使用

避坑2:

全局变量的方式,不要用在线程和线程之间

读线程可能频繁 “读不到数据”(因为写标记被占用),数据更新率下降。

无原子性保护:状态标记本身的读写可能被打断,导致逻辑异常。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/2/25 19:35:33

通义万相 (Wan) 2.6 API 可视化调用面板

通义万相图像生成模型支持图像编辑、图文混排输出&#xff0c;满足多样化生成与集成需求。 以下是一个基于 React 的通义万相 (Wan) API 可视化调用面板。 效果图&#xff1a; 由于目前通义万相&#xff08;Wan 2.1/2.0&#xff09;的视频生成通常是异步任务&#xff08;提…

作者头像 李华
网站建设 2026/3/11 1:20:35

bsdiff/bspatch:高效二进制差异补丁技术深度解析

bsdiff/bspatch&#xff1a;高效二进制差异补丁技术深度解析 【免费下载链接】bsdiff bsdiff and bspatch are libraries for building and applying patches to binary files. 项目地址: https://gitcode.com/gh_mirrors/bs/bsdiff 你是否曾为大型软件更新包下载耗时过…

作者头像 李华
网站建设 2026/3/11 14:54:05

高温环境下PCB线宽与电流对照表修正方法探讨

高温环境下如何科学修正PCB线宽与电流关系&#xff1f;工程师不可忽视的热设计实战指南你有没有遇到过这样的情况&#xff1a;明明按照标准PCB线宽与电流对照表选了走线宽度&#xff0c;结果样机一上电&#xff0c;大电流路径附近的铜皮就开始发烫&#xff0c;甚至在高温老化测…

作者头像 李华
网站建设 2026/3/5 19:01:53

学术论文免费获取神器:三分钟搞定Sci-Hub X Now完整部署

学术论文免费获取神器&#xff1a;三分钟搞定Sci-Hub X Now完整部署 【免费下载链接】sci-hub-now 项目地址: https://gitcode.com/gh_mirrors/sc/sci-hub-now 还在为昂贵的论文下载费用发愁吗&#xff1f;想不想拥有一个能让你随时随地免费获取学术文献的神奇工具&…

作者头像 李华
网站建设 2026/3/4 19:13:06

UDS多帧传输在CANoe中的模拟与验证

UDS多帧传输在CANoe中的实战模拟与深度验证从一个诊断失败说起&#xff1a;为什么我们需要关注多帧&#xff1f;某日&#xff0c;一位工程师在刷写ECU软件时发现&#xff0c;当请求读取某个大尺寸DID&#xff08;数据标识符&#xff09;时&#xff0c;响应总是被截断或直接超时…

作者头像 李华
网站建设 2026/3/8 17:52:42

AI数独照片解题:终极免费智能工具完整指南

AI_Sudoku是一款革命性的智能数独解题工具&#xff0c;它能从普通照片中自动识别数独题目并快速给出准确答案。这款终极AI助手结合了先进的计算机视觉和深度学习技术&#xff0c;为数独爱好者提供了前所未有的便捷解题体验。无论你是数独新手还是资深玩家&#xff0c;这款免费工…

作者头像 李华