C语言中基于信号量的线程同步问题:公交模拟中学生下车异常的修复求助
兄弟,我看了你的代码和问题描述,核心问题出在条件变量的信号发送方式、临界区的保护逻辑上,还有信号量的用法也存在逻辑偏差,咱们一步步拆解问题并给出修复方案:
核心问题分析
条件变量唤醒方式错误
你在公交routine里用了pthread_cond_signal,这个函数只会唤醒单个等待该条件的线程。但公交到站后,应该是所有在对应站点等车/准备下车的学生都能收到通知,所以用signal就会导致大部分学生线程一直处于等待状态——这就是你看到只有Student 1下车的直接原因!共享资源未正确加锁
你修改bus->department_capacity、stop_A、bus_array这些全局共享资源时,没有用互斥锁保护,多个线程同时操作这些资源会引发竞态条件,导致部门人数计数错误、上下车状态混乱等问题。信号量用法逻辑错误
你在学生routine开头就调用sem_wait(&semaphore),直到整个学生流程(上车、上学、放学、回家)结束才sem_post,这相当于同一时间只能有一个学生线程运行,完全违背了多学生并发的场景设计。如果这个信号量是用来限制同时存在的学生数量,逻辑也应该放在线程创建阶段,而不是routine内部。
具体修复步骤
1. 替换条件变量的唤醒方式
把公交routine里的pthread_cond_signal全部改成pthread_cond_broadcast,这样公交到站后会唤醒所有等待该站点条件的线程,同时调整位置更新的顺序(确保状态先更新再通知):
// 公交到A站的代码修改 bus->bus_position = Stop_A; // 先更新位置 printf(ANSI_BLUE "Bus arrived at Stop A" ANSI_RESET "\n" ); print_areas(); pthread_cond_broadcast(&cond_A); // 广播唤醒所有等A站的线程 sleep(BUS_WAIT_TIME); // 公交到B站的代码修改同理 bus->bus_position = Stop_B; printf(ANSI_BLUE "Bus arrived at Stop B" ANSI_RESET "\n"); print_areas(); pthread_cond_broadcast(&cond_B); sleep(BUS_WAIT_TIME);
2. 给所有共享资源操作加锁保护
所有修改全局共享数据的代码,都要放在pthread_mutex_lock和pthread_mutex_unlock之间,比如学生从A站上车的逻辑:
// 学生从A站上车的修改后代码 pthread_mutex_lock(&mutex); // 锁内检查条件,确保线程安全 while(bus->bus_position != Stop_A || !check_department(student)){ pthread_cond_wait(&cond_A, &mutex); } // 修改共享资源 bus->department_capacity[student->department]--; stop_A[student->AM - 1] = -1; bus_array[student->AM - 1] = student->AM; pthread_mutex_unlock(&mutex); printf(ANSI_GREEN "Student %d is boarding the bus from Stop A" ANSI_RESET "\n", student->AM); print_areas();
同理,学生下车、从B站上车、回家时修改共享资源的操作,都要加上互斥锁保护。
3. 调整信号量的用法(或移除)
如果你的信号量是用来限制同时运行的学生线程总数,应该把sem_wait放在创建学生线程之前,sem_post放在学生线程结束后:
// 主线程创建学生线程时的逻辑示例 for(int i=0; i<4; i++){ sem_wait(&semaphore); pthread_create(&student_threads[i], NULL, student_routine, (void*)&student_ids[i]); } // 学生routine末尾只保留sem_post sem_post(&semaphore); return NULL;
如果不需要限制线程总数,直接移除这个信号量即可,因为互斥锁+条件变量已经能满足同步需求。
4. 修正学生ID的传递逻辑
你在search_node(student_list, (int)argv)里把指针直接转成int,这在64位系统上会有截断问题,应该改成:
int student_id = *(int*)argv; struct student* student = search_node(student_list, student_id);
额外注意点
pthread_cond_wait会自动释放互斥锁,被唤醒后会重新获取锁,所以你的条件检查必须放在while循环里(这点你做对了,要保持,因为线程被唤醒后可能条件已经不满足了)。
按照这些修改,你应该能解决只有单个学生下车的问题,同时让整个公交模拟的同步逻辑正常运行。
备注:内容来源于stack exchange,提问作者Jukeland

