C语言多线程窄桥通行实现中numOfCarsOnRoad计数不一致问题咨询
问题根因与修复方案
问题1:numOfCarsOnRoad计数异常原因
- 线程创建传参错误:创建线程时传入了循环变量
i的地址,所有线程共享同一块内存空间,循环执行过程中i的值持续变化,最终大部分线程拿到的id都是错误值,直接导致输出混乱。 - 共享变量无同步保护:
numOfCarsOnRoad、currActiveDir、carsGoingW、carsGoingE都是多线程共享的全局变量,你在临界区(sem_wait保护区域)外直接读写这些变量,会产生竞态条件,读到的数值均为脏数据。 - 等待判断逻辑位置错误:你把道路状态的判断逻辑放在了信号量获取之前,多个线程可能同时通过判断条件后等待信号量,等拿到信号量时道路状态已经发生变化,最终计数不符合预期。
问题2:sleep(1)后线程执行顺序和启动顺序不一致原因
sleep(1)的作用是让线程至少阻塞1秒,而非1秒后立刻获得CPU执行权。操作系统的线程调度是抢占式的,计时结束的线程需要等待调度器分配CPU时间片,执行顺序不存在必然的先后关系。如果需要保证同方向车辆的通行顺序,可以额外实现FIFO等待队列,按申请顺序放行车辆即可,不需要强制依赖启动顺序。
修复代码
头文件 ask2.h
void * crossBridge(void *i); void parseCarArg(int argc, char *argv[]);
主实现代码
#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <pthread.h> #include <semaphore.h> #include <string.h> #include "ask2.h" sem_t sem; int numOfCarsOnRoad = 0; int carsGoingW = 0; int carsGoingE = 0; long numOfCars = 0; // used for thread initializations char currActiveDir = '_'; // either W or E ( _ initially) pthread_mutex_t rand_mutex = PTHREAD_MUTEX_INITIALIZER; // 保护rand调用 void *crossBridge(void *arg) { int id = (int)arg; // 直接拿传过来的值,不再解引用地址 char direction[5]; // 线程安全的rand调用 pthread_mutex_lock(&rand_mutex); if (rand() % 2 == 0) { strcpy(direction, "West"); } else { strcpy(direction, "East"); } pthread_mutex_unlock(&rand_mutex); printf("Car #%d waiting to pass to the %s...\n", id, direction); sem_wait(&sem); // 先拿信号量再操作共享变量 // 临界区内判断状态,不符合就释放信号量等待重试 while (currActiveDir != '_' && currActiveDir != direction[0] || numOfCarsOnRoad == 3) { sem_post(&sem); sleep(1); sem_wait(&sem); } // 更新方向和等待计数 if (currActiveDir == '_') { currActiveDir = direction[0]; } if (direction[0] == 'W') carsGoingW++; else carsGoingE++; numOfCarsOnRoad++; printf("Car #%d going to the %s. Number of cars on the road = %d\n", id, direction, numOfCarsOnRoad); sem_post(&sem); // 操作完共享变量就释放信号量,不要把sleep放在临界区里 sleep(1); // 模拟通行时间,不需要放在临界区 sem_wait(&sem); // 再次拿信号量修改状态 numOfCarsOnRoad--; printf("Car #%d crossed to the %s! Number of cars on the road = %d\n", id, direction, numOfCarsOnRoad); if(direction[0] == 'W') carsGoingW--; else carsGoingE--; // 避免饥饿逻辑 if (numOfCarsOnRoad == 0) { if (currActiveDir == 'W' && carsGoingE > 0) currActiveDir = 'E'; else if (currActiveDir == 'E' && carsGoingW > 0) currActiveDir = 'W'; else currActiveDir = '_'; } sem_post(&sem); pthread_exit(NULL); } void parseCarArg(int argc, char *argv[]) { int i; for (i = 0; i < argc; i++) { if (strcmp(argv[i], "-c") == 0) { if (++i < argc && strlen(argv[i]) > 0) numOfCars = strtol(argv[i], NULL, 10); // convert to long if (numOfCars <= 0) { perror("You must enter a number of cars > 0!\n"); exit(EXIT_FAILURE); } break; } } } int main(int argc, char *argv[]) { if (argc < 2) exit(EXIT_FAILURE); parseCarArg(argc, argv); srand(time(NULL)); // seed the generator using epoch time if (sem_init(&sem, 0, 1) == -1) // 信号量改作互斥锁用,保护所有共享变量,最大通行3辆的逻辑用numOfCarsOnRoad判断 { perror("Failed to initialize semaphore!\n"); exit(EXIT_FAILURE); } pthread_t cars[numOfCars]; int i; for (i = 0; i < numOfCars; i++) { // 直接传i的值,不要传地址 if (pthread_create(&cars[i], NULL, crossBridge, (void*)i) != 0) { perror("Failed to create threads for the cars!\n"); exit(EXIT_FAILURE); } } // wait for all threads to finish for (i = 0; i < numOfCars; i++) pthread_join(cars[i], NULL); sem_destroy(&sem); pthread_mutex_destroy(&rand_mutex); return 0; }
内容的提问来源于stack exchange,提问作者Stelios Papamichail
相关产品推荐
相关产品推荐

