多线程C语言超市模拟程序补货同步问题排查求助
多线程超市模拟库存异常问题排查与修复
问题背景
你开发的多线程C语言超市模拟程序包含三类线程:Manager(触发补货)、Employee(执行补货)、Stock Checker(监控库存),虽已用mutex、信号量、条件变量做同步,但出现两个核心问题:
- 部分商品库存超出设定上限(如商品5库存达23,上限为20)
- 部分商品被连续重复补货(如商品8)
核心原因分析
1. 库存操作的原子性缺失
补货流程中库存检查、补货决策、库存更新三个环节没有被完整的互斥锁保护,导致竞态条件:
- 比如Manager线程判断商品库存低于阈值后触发补货,但在Employee线程执行补货前,另一个Employee线程可能已经完成补货,导致重复累加库存;
- 多个Employee线程同时读取同一商品的当前库存,各自计算补货量并写入,最终库存超出上限。
2. 补货状态未做互斥标记
没有为商品设置“补货中”的状态位,导致Manager/Stock Checker线程在商品正在补货时,再次检测到库存不足并触发新的补货请求,造成重复补货。
3. 互斥锁范围错误
很多场景下只在写入库存时加锁,而读取库存、判断补货条件的环节没有加锁,导致判断和执行之间的间隙被其他线程打断,出现逻辑错误。
具体修复方案
1. 原子化库存操作流程
把所有涉及库存读写、补货决策的逻辑都放在同一个互斥锁保护范围内,确保整个操作流程不会被其他线程打断。
示例修正代码:
// 全局变量:库存数组、补货状态数组、互斥锁 int stock[ITEM_COUNT]; _Bool is_restocking[ITEM_COUNT]; pthread_mutex_t stock_mutex; // Employee线程补货逻辑 void* employee_thread(void* arg) { while (1) { int item_id = wait_restock_sem(); // 等待补货信号(自定义信号量等待逻辑) pthread_mutex_lock(&stock_mutex); // 检查当前库存是否真的需要补货 if (stock[item_id] >= MAX_STOCK) { is_restocking[item_id] = 0; pthread_mutex_unlock(&stock_mutex); continue; } int restock_amount = MAX_STOCK - stock[item_id]; stock[item_id] += restock_amount; printf("商品%d补货完成,库存:%d\n", item_id, stock[item_id]); is_restocking[item_id] = 0; // 取消补货标记 pthread_mutex_unlock(&stock_mutex); } return NULL; } // Manager线程触发补货逻辑 void* manager_thread(void* arg) { while (1) { sleep(CHECK_INTERVAL); pthread_mutex_lock(&stock_mutex); for (int i = 0; i < ITEM_COUNT; i++) { // 只有库存低于阈值且未在补货时,才触发补货 if (stock[i] < RESTOCK_THRESHOLD && !is_restocking[i]) { is_restocking[i] = 1; post_restock_sem(i); // 发送补货信号 } } pthread_mutex_unlock(&stock_mutex); } return NULL; }
2. 新增补货状态标记
为每个商品添加is_restocking布尔数组,在触发补货时标记为1,补货完成后重置为0,避免重复触发补货请求。
3. 修正Stock Checker线程的监控逻辑
Stock Checker读取库存时必须加锁,同时显示补货状态,避免误判:
void* stock_checker_thread(void* arg) { while (1) { sleep(MONITOR_INTERVAL); pthread_mutex_lock(&stock_mutex); puts("=== 库存监控快照 ==="); for (int i = 0; i < ITEM_COUNT; i++) { printf("商品%d:%d %s\n", i, stock[i], is_restocking[i] ? "(补货中)" : ""); } pthread_mutex_unlock(&stock_mutex); } return NULL; }
额外注意事项
- 不要在互斥锁保护范围内执行sleep、IO等耗时操作,避免阻塞其他线程访问共享资源;
- 测试时可以增加详细日志,记录每个线程操作商品的时间、操作前后的库存值,快速定位竞态条件;
- 如果使用信号量,确保每个商品的补货信号只被发送一次,直到补货完成后才能再次触发。
内容的提问来源于stack exchange,提问作者wesam
相关产品推荐
相关产品推荐

