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

基于pthreads的C多线程公交程序:乘客到站不下车问题排查

公交仿真程序下车异常排查问题

问题背景

用C语言的pthreads库写了个多线程公交仿真程序,包含4个公交站线程、10个独立乘客线程和1个公交线程。初始化时乘客随机前往公交站并确定下车站点,公交按顺序途经站点接载乘客,乘客要在目的站点下车。目前上车逻辑正常,但乘客到站后经常不下车,还没处理已下车线程的销毁,也没校验车内乘客数量,得排查这个异常原因。

程序代码

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

#define PASSENGER_NUMBER 10
#define BUS_SEATS 20
#define BUS_STOPS 4

pthread_mutex_t bus;
pthread_mutex_t busStation;

pthread_cond_t signal_bus_arrived = PTHREAD_COND_INITIALIZER; // 通知公交站线程公交到站
pthread_cond_t signal_bus_depart = PTHREAD_COND_INITIALIZER; // 通知公交可以发车
pthread_cond_t signal_board = PTHREAD_COND_INITIALIZER; // 通知站点乘客上车
pthread_cond_t signal_exit = PTHREAD_COND_INITIALIZER; // 通知车内乘客下车

int bus_passenger_number = 0;
int busStopCounter[BUS_STOPS] = {0, 0, 0, 0};
int passengersToGetOut[BUS_STOPS] = {0};
int currentStop = 1;
int loop = 0;
int nextStop = 2;

void *busStopp(void *args) {
    int id = (intptr_t) args;
    while (1) {
        pthread_mutex_lock(&bus);
        pthread_cond_wait(&signal_bus_arrived, &bus);
        pthread_mutex_unlock(&bus);

        if (id == currentStop) {
            // 先通知下车
            pthread_cond_broadcast(&signal_exit);
            // 等待所有需要下车的乘客完成
            pthread_mutex_lock(&bus);
            while (passengersToGetOut[id - 1] > 0) {
                pthread_cond_wait(&signal_bus_depart, &bus);
            }
            pthread_mutex_unlock(&bus);

            // 再通知上车
            if (busStopCounter[id - 1] > 0) {
                pthread_cond_broadcast(&signal_board);
                // 等待所有站点乘客上车完成
                pthread_mutex_lock(&bus);
                while (busStopCounter[id - 1] > 0) {
                    pthread_cond_wait(&signal_bus_depart, &bus);
                }
                pthread_mutex_unlock(&bus);
            }
            // 通知公交可以发车
            pthread_cond_broadcast(&signal_bus_depart);
        }
    }
}

void *passenger(void *args) {
    int id = (intptr_t) args;
    int passangerStop = (rand() % 4 + 1);
    int exitPassengerStop = (rand() % 4 + 1);
    while (exitPassengerStop == passangerStop) {
        exitPassengerStop = (rand() % 4 + 1);
    }
    int isRiding = 0;
    pthread_mutex_lock(&bus);
    busStopCounter[passangerStop - 1]++;
    pthread_mutex_unlock(&bus);
    printf("乘客%d来到站点%d,下车站点:%d\n", id, passangerStop, exitPassengerStop);

    while (1) {
        if (!isRiding) {
            // 等待上车信号
            pthread_mutex_lock(&busStation);
            pthread_cond_wait(&signal_board, &busStation);
            pthread_mutex_unlock(&busStation);

            pthread_mutex_lock(&bus);
            if (passangerStop == currentStop) {
                bus_passenger_number++;
                busStopCounter[passangerStop - 1]--;
                printf("乘客%d登上公交\n", id);
                isRiding = 1;
                // 记录下一站需要下车的人数(如果下一站是目的站)
                if (exitPassengerStop == nextStop) {
                    passengersToGetOut[nextStop - 1]++;
                }
                // 如果站点没人了,通知公交可以发车
                if (busStopCounter[passangerStop - 1] == 0) {
                    pthread_cond_broadcast(&signal_bus_depart);
                }
            }
            pthread_mutex_unlock(&bus);
        } else {
            // 等待下车信号
            pthread_mutex_lock(&bus);
            pthread_cond_wait(&signal_exit, &bus);
            pthread_mutex_unlock(&bus);

            pthread_mutex_lock(&bus);
            if (currentStop == exitPassengerStop) {
                printf("乘客%d在站点%d下车\n", id, currentStop);
                isRiding = 0;
                bus_passenger_number--;
                passengersToGetOut[currentStop - 1]--;
                // 通知公交下车完成
                pthread_cond_broadcast(&signal_bus_depart);
                // 下车后线程退出
                pthread_mutex_unlock(&bus);
                pthread_exit(NULL);
            }
            pthread_mutex_unlock(&bus);
        }
    }
}

void *busDriving(void *args) {
    sleep(2);
    while (loop < 10) {
        pthread_mutex_lock(&bus);
        printf("公交到达站点%d\n", currentStop);
        // 通知所有公交站线程公交到站
        pthread_cond_broadcast(&signal_bus_arrived);
        // 等待站点处理完成(上下车)
        pthread_cond_wait(&signal_bus_depart, &bus);

        printf("当前站点 = %d\n", currentStop);
        printf("公交内乘客数:%d\n", bus_passenger_number);
        pthread_mutex_unlock(&bus);

        sleep(2);
        pthread_mutex_lock(&bus);
        currentStop++;
        nextStop++;

        if (currentStop == 5) {
            currentStop = 1;
            loop++;
        }
        if (currentStop == 4) {
            nextStop = 1;
        }
        pthread_mutex_unlock(&bus);
    }

    printf("--------公交返回车库-------- 循环次数:%d\n", loop);
    // 终止所有线程(这里可以通过全局标志实现,简化示例暂不处理)
    pthread_exit(NULL);
}

int main() {
    srand(time(NULL));
    pthread_t busDriver;
    pthread_t passengerID[PASSENGER_NUMBER];
    pthread_t busStopID[BUS_STOPS];

    pthread_mutex_init(&bus, NULL);
    pthread_mutex_init(&busStation, NULL);

    for (int i = 0; i < BUS_STOPS; i++) {
        pthread_create(&busStopID[i], NULL, busStopp, (void *) (intptr_t) (i+1));
    }

    for (int i = 0; i < PASSENGER_NUMBER; i++) {
        pthread_create(&passengerID[i], NULL, passenger, (void *) (intptr_t) (i+1));
    }

    pthread_create(&busDriver, NULL, busDriving, NULL);

    pthread_join(busDriver, NULL);
    // 这里可以添加终止其他线程的逻辑,避免无限等待
    return 0;
}

当前输出示例

乘客1来到站点4,下车站点:2
乘客2来到站点1,下车站点:2
乘客3来到站点3,下车站点:2
乘客4来到站点3,下车站点:4
乘客5来到站点2,下车站点:3
乘客6来到站点2,下车站点:4
乘客7来到站点3,下车站点:2
乘客8来到站点2,下车站点:1
乘客9来到站点2,下车站点:1
乘客10来到站点2,下车站点:4
公交到达站点1
乘客2登上公交
当前站点 = 1
公交内乘客数:1
公交到达站点2
乘客6登上公交
乘客8登上公交
乘客10登上公交
乘客9登上公交
乘客5登上公交
当前站点 = 2
公交内乘客数:6
公交到达站点3
乘客7登上公交
乘客4登上公交
乘客5在站点3下车
乘客3登上公交
...

期望输出示例

乘客1来到站点4,下车站点:2
乘客2来到站点1,下车站点:2
乘客3来到站点3,下车站点:2
乘客4来到站点3,下车站点:4
乘客5来到站点2,下车站点:3
乘客6来到站点2,下车站点:4
乘客7来到站点3,下车站点:2
乘客8来到站点2,下车站点:1
乘客9来到站点2,下车站点:1
乘客10来到站点2,下车站点:4
公交到达站点1
乘客2登上公交
当前站点 = 1
公交内乘客数:1
公交到达站点2
乘客2在站点2下车
乘客6登上公交
乘客8登上公交
乘客10登上公交
乘客9登上公交
乘客5登上公交
当前站点 = 2
公交内乘客数:5
公交到达站点3
乘客7登上公交
乘客4登上公交
乘客5在站点3下车
乘客3登上公交
当前站点 = 3
公交内乘客数:7
...

问题原因分析

  1. 下车触发逻辑错误:原代码里,乘客的下车判断依赖上车信号,但已经在车上的乘客不会收到这个信号,自然不会触发下车检查,导致到站不下车。
  2. 流程顺序颠倒:公交到站后直接触发上车流程,没有先处理下车操作,违反了“先下后上”的实际逻辑。
  3. 全局变量访问无保护:currentStop等变量的修改和读取没有在锁的保护下,线程间可能出现数据不一致,导致乘客判断站点错误。
  4. 变量赋值逻辑冗余:passengersToGetOut[nextStop-1] = passengersToGetOut[nextStop-1] +=1;这种重复赋值会引发逻辑混乱,应该直接用+=1。
  5. 下车后线程未终止:乘客下车后仍在循环等待信号,既浪费资源,也可能导致后续错误触发。

修复要点

  1. 新增下车信号:添加signal_exit条件变量,公交到站后先广播该信号,触发车内乘客的下车检查。
  2. 调整站务流程:公交到站后,先处理下车操作,等待所有需要下车的乘客完成后,再处理上车操作,最后通知公交发车。
  3. 锁保护全局变量:所有对currentStop、bus_passenger_number等全局变量的读写操作,都要在对应的互斥锁保护下进行,避免数据竞争。
  4. 修正变量赋值:去掉冗余的赋值语句,确保passengersToGetOut的计数正确。
  5. 终止下车乘客线程:乘客下车后调用pthread_exit(NULL)结束线程,避免无效循环。
  6. 添加人数校验:可以在上下车后检查bus_passenger_number是否在0到BUS_SEATS之间,确保数据正确。

内容的提问来源于stack exchange,提问作者Cezary

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.24 22:39:52