C语言POSIX信号量多线程同步作业死锁问题求助
多线程同步死锁问题:线程10结束后剩余线程无法全部启动
作业要求
创建40个线程,并基于以下条件同步:
- 主线程需等待所有其他线程结束后才能终止
- 同一时刻最多6个线程处于运行状态
- 线程10仅当包含自身在内的6个线程处于运行状态时才能终止
补充定义:此处的“运行”指线程输出BEGIN日志后、END日志前的阶段,即线程10输出END时,必须恰好有另外5个线程已输出BEGIN并处于等待状态。
问题描述
使用POSIX信号量实现同步,已满足前两个条件,但线程10达成终止条件后,剩余线程无法全部启动,最终陷入死锁。
当前代码实现
全局变量与信号量初始化
#include <pthread.h> #include <semaphore.h> int p2_t_count = 0; sem_t sem_t2; sem_t sem_t210; sem_t sem_kidnap; // 注:缺少p2_t_count的同步锁 // 信号量初始化代码 if(sem_init(&sem_t2, 0, 5) < 0){ perror("Error initializing the semaphore!"); exit(2); } if(sem_init(&sem_t210, 0, 0) < 0){ perror("Error initializing the semaphore!"); exit(2); } if(sem_init(&sem_kidnap, 0, 0) < 0){ perror("Error initializing the semaphore!"); exit(2); }
普通线程逻辑
void* thread_barrier(void* arg){ struct thread_arg *thread_info = (struct thread_arg*)arg; // 劫持前5个线程,直到线程10结束才允许它们输出END if(p2_t_count < 5){ sem_wait(&sem_t2); info(BEGIN, thread_info->process_n, thread_info->thread_n); p2_t_count++; if(p2_t_count == 5){ // 当有5个线程进入运行状态时,解锁线程10 sem_post(&sem_t210); } sem_wait(&sem_kidnap); info(END, thread_info->process_n, thread_info->thread_n); return NULL; } // 剩余34个线程的逻辑 sem_wait(&sem_t2); info(BEGIN, thread_info->process_n, thread_info->thread_n); info(END, thread_info->process_n, thread_info->thread_n); sem_post(&sem_t2); return NULL; }
线程10的逻辑
void* thread_t210(void* arg){ sem_post(&sem_t210); info(BEGIN, 2, 10); sem_wait(&sem_t210); sem_wait(&sem_t210); // 此处多余的wait导致线程10阻塞 info(END, 2, 10); // 释放被劫持的线程 for(int i = 0; i < 5; i++){ sem_post(&sem_kidnap); } // 尝试启动新线程 for(int i = 0; i < 5; i++){ sem_post(&sem_t2); } return NULL; }
问题分析
- 线程10自身阻塞:线程10中存在多余的
sem_wait(&sem_t210)调用。初始sem_t210值为0,线程10先执行sem_post使其变为1,随后第一次sem_wait将其重置为0,第二次sem_wait会导致线程10永久阻塞,无法执行到释放其他线程的逻辑,直接引发死锁。 - 全局变量竞争:
p2_t_count的读写未加同步,多个线程可能同时判断p2_t_count <5并进入劫持分支,导致超过5个线程等待sem_kidnap,但后续仅post5次,剩余线程会永久等待。 - 信号量计数异常:由于前两个问题,线程10无法执行到
sem_post(&sem_t2)的逻辑,剩余线程无法获取信号量启动。
解决方案
1. 修正线程10的逻辑
移除多余的sem_wait,让线程10等待前5个线程准备就绪后再启动:
void* thread_t210(void* arg){ sem_wait(&sem_t210); // 等待前5个线程全部进入运行状态 info(BEGIN, 2, 10); // 此时恰好有5个线程已输出BEGIN并等待sem_kidnap,加上线程10共6个运行线程 info(END, 2, 10); // 满足终止条件 // 释放被劫持的线程 for(int i = 0; i < 5; i++){ sem_post(&sem_kidnap); } // 补充sem_t2的名额(前5个线程未释放sem_t2) for(int i = 0; i < 5; i++){ sem_post(&sem_t2); } return NULL; }
2. 为p2_t_count添加同步锁
确保对p2_t_count的读写是原子操作,避免多个线程进入劫持分支:
// 添加全局互斥锁 pthread_mutex_t count_mutex; // 初始化互斥锁(在信号量初始化后) if(pthread_mutex_init(&count_mutex, NULL) != 0){ perror("Error initializing mutex!"); exit(2); } // 修改thread_barrier函数 void* thread_barrier(void* arg){ struct thread_arg *thread_info = (struct thread_arg*)arg; int is_kidnap = 0; // 原子判断并更新计数 pthread_mutex_lock(&count_mutex); if(p2_t_count < 5){ is_kidnap = 1; p2_t_count++; } pthread_mutex_unlock(&count_mutex); if(is_kidnap){ sem_wait(&sem_t2); info(BEGIN, thread_info->process_n, thread_info->thread_n); // 检查是否是第5个被劫持的线程 pthread_mutex_lock(&count_mutex); int current_count = p2_t_count; pthread_mutex_unlock(&count_mutex); if(current_count == 5){ sem_post(&sem_t210); // 解锁线程10 } sem_wait(&sem_kidnap); info(END, thread_info->process_n, thread_info->thread_n); return NULL; } // 剩余线程逻辑不变 sem_wait(&sem_t2); info(BEGIN, thread_info->process_n, thread_info->thread_n); info(END, thread_info->process_n, thread_info->thread_n); sem_post(&sem_t2); return NULL; }
3. 确保主线程等待所有线程
主线程需要调用pthread_join等待所有40个线程终止,例如:
pthread_t tids[40]; // 创建所有线程(包括线程10)的代码... // 等待每个线程结束 for(int i = 0; i < 40; i++){ pthread_join(tids[i], NULL); } // 销毁信号量和互斥锁 sem_destroy(&sem_t2); sem_destroy(&sem_t210); sem_destroy(&sem_kidnap); pthread_mutex_destroy(&count_mutex);
内容的提问来源于stack exchange,提问作者user_toni
相关产品推荐
相关产品推荐

