You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

C语言pthread多线程使用二进制信号量仅单线程可获取问题咨询

首先需要先指出你示例代码中的笔误:main函数里的sem_init(&sem.ctx, 0, 1);拼写错误,正确写法为sem_init(&ctx.sem, 0, 1);,否则代码无法正常编译,属于编写示例时的手误。

问题核心原因

这个现象的本质是POSIX 信号量默认采用非公平实现,结合线程调度的上下文开销导致了单线程饥饿,具体逻辑如下:

  • Linux 原生的 POSIX 信号量没有公平性强制要求,不会按照线程等待锁的先后顺序分配信号量,允许正在 CPU 上运行的线程在释放信号量后立刻"插队"重新获取,不需要优先给等待队列里的阻塞线程分配。
  • 你的代码中sleep(1)是在信号量持有区间(sem_wait和sem_post之间)执行,这1秒内其余9个线程都会阻塞在sem_wait调用上,进入内核的休眠等待队列。
  • 当持有信号量的线程执行完sleep、打印、计数器自增操作,调用sem_post释放信号量后,会立刻进入下一轮循环的sem_wait调用:此时信号量值刚被置为1,当前线程正处于运行态,仅需要一次用户态的原子操作就能拿到信号量;而等待队列里的阻塞线程从内核休眠态唤醒、完成上下文切换需要几百纳秒到几微秒的开销,完全赶不上当前线程的"插队"速度,所以永远抢不到信号量。

为什么删除sleep后表现正常

删除sleep后,信号量持有区间的逻辑只剩下计数器判断、printf打印和自增操作,其中printf是会触发用户态到内核态切换的系统调用,执行开销足够大:

  • 要么printf的执行消耗完了当前线程的时间片,线程在sem_post之后被内核调度切走,给其他等待线程让出了CPU;
  • 要么printf的执行开销足够让等待队列里的其他线程完成唤醒、上下文切换,在当前线程下一轮调用sem_wait之前就抢到了信号量,因此呈现出线程调度随机的预期表现。

修复方案

如果需要保证多线程能轮流获取信号量,可以选择两种方案:

  • 改用带公平性保证的同步原语,比如设置了PTHREAD_MUTEX_FAIR_NP属性的互斥锁(Linux 特有,非 POSIX 标准)
  • 在sem_post之后、下一轮sem_wait之前调用sched_yield()主动让出CPU,给其他等待线程运行的机会。

内容的提问来源于stack exchange,提问作者Daniel Walker

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.11 08:12:02