You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

多线程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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.02 15:15:56