基于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 ...
问题原因分析
- 下车触发逻辑错误:原代码里,乘客的下车判断依赖上车信号,但已经在车上的乘客不会收到这个信号,自然不会触发下车检查,导致到站不下车。
- 流程顺序颠倒:公交到站后直接触发上车流程,没有先处理下车操作,违反了“先下后上”的实际逻辑。
- 全局变量访问无保护:
currentStop等变量的修改和读取没有在锁的保护下,线程间可能出现数据不一致,导致乘客判断站点错误。 - 变量赋值逻辑冗余:
passengersToGetOut[nextStop-1] = passengersToGetOut[nextStop-1] +=1;这种重复赋值会引发逻辑混乱,应该直接用+=1。 - 下车后线程未终止:乘客下车后仍在循环等待信号,既浪费资源,也可能导致后续错误触发。
修复要点
- 新增下车信号:添加
signal_exit条件变量,公交到站后先广播该信号,触发车内乘客的下车检查。 - 调整站务流程:公交到站后,先处理下车操作,等待所有需要下车的乘客完成后,再处理上车操作,最后通知公交发车。
- 锁保护全局变量:所有对
currentStop、bus_passenger_number等全局变量的读写操作,都要在对应的互斥锁保护下进行,避免数据竞争。 - 修正变量赋值:去掉冗余的赋值语句,确保
passengersToGetOut的计数正确。 - 终止下车乘客线程:乘客下车后调用
pthread_exit(NULL)结束线程,避免无效循环。 - 添加人数校验:可以在上下车后检查
bus_passenger_number是否在0到BUS_SEATS之间,确保数据正确。
内容的提问来源于stack exchange,提问作者Cezary
相关产品推荐
相关产品推荐

