C++实现生产者消费者模式时生产者连续打印3次异常排查
生产者消费者进程通信连续发送异常分析与修复
异常原因
- 信号量创建参数错误:代码中
sem_id = semget(IPC_PRIVATE, m, 0600);第二个参数传入的是生产者数量m,但实际使用了3个信号量(互斥锁PISI、空槽位PUN、满槽位PRAZAN),如果生产者数量小于3,会导致信号量访问越界,信号量控制逻辑完全失效。 - 临界区内放置sleep阻塞逻辑:生产者拿到互斥锁
PISI完成数据写入后,在临界区内执行sleep(1),占用锁的期间其他进程无法操作缓冲区,释放锁后如果当前生产者再次抢到锁,就会出现连续发送的情况。 - 消费者消费逻辑延迟:消费者在申请满槽位信号量
PRAZAN前先执行sleep(1),导致消费速度远慢于生产速度,长度为5的缓冲区很快被生产者填满,自然会出现多个连续的生产者发送日志。 - 总数据量计算错误:main函数中初始化完共享内存后执行了
m++(用于适配fork消费者+生产者的总进程数),但消费者循环判断条件i < n*m用了自增后的m,导致消费者预期接收的总数据量比实际多,同时子进程执行完业务逻辑后没有主动退出,会产生多余进程干扰正常输出。
修复方案
- 修正信号量创建参数:将semget的第二个参数固定为3,保证3个信号量都能正常初始化:
sem_id = semget(IPC_PRIVATE, 3, 0600);
- 调整sleep的位置到临界区外:生产者释放完所有信号量后再执行sleep,消费者申请信号量前不要sleep,消费完成释放信号量后再sleep,保证生产消费速度匹配。
修改后生产者核心逻辑:
do { ispitaj(PUN); ispitaj(PISI); medo->meduspremnik[medo->ULAZ] = rand() % 1000; cout << "Proizvodac " << neka << " salje: " << "''" << medo->meduspremnik[medo->ULAZ] << "''" << endl; medo->ULAZ = (medo->ULAZ + 1) % 5; postavi(PISI); postavi(PRAZAN); sleep(1); // sleep移到临界区外 i = i + 1; }while(i < n);
修改后消费者核心逻辑:
do { ispitaj(PRAZAN); // 去掉开头的sleep,直接申请信号量 cout << "Potrosac prima " << medo->meduspremnik[medo->IZLAZ] << "." << endl; zbroj += medo->meduspremnik[medo->IZLAZ]; medo->IZLAZ = (medo->IZLAZ+1) % 5; postavi(PUN); sleep(1); // 消费完成后再加sleep i = i+1; }while (i < n * m);
- 单独存储总进程数,不要修改原始生产者数量m,同时子进程执行完逻辑后主动退出:
int m = atoi(argv[1]); // 原始生产者数量,后续不再修改 int n = atoi(argv[2]); int process_cnt = m + 1; // 总进程数=生产者数+1个消费者 // 省略共享内存、信号量初始化逻辑 for(int i = 0; i < process_cnt; i++) { switch(fork()) { case 0: if(i == 0) { potrosac(); } else { proizvodac(i); } exit(0); // 子进程执行完直接退出,避免执行后续逻辑 case -1: perror("fork error"); exit(1); } }
内容的提问来源于stack exchange,提问作者Exch
相关产品推荐
相关产品推荐

