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

基于futex实现信号量时futex_wake返回0的问题排查

问题分析与解决方案

首先,你遇到的futex_wake返回0导致断言失败的问题,核心原因是你错误地假设了调用futex_wake时一定存在等待的线程,但实际上即使信号量的value在sem_post前小于等于0,也可能没有线程在futex_wait上阻塞,这是完全合法的场景。

为什么会出现这种情况?

举个典型的竞态场景:

  1. 线程A调用sem_wait,原子性地将信号量value从0减到-1,此时它准备调用futex_wait进入等待。
  2. 就在线程A执行futex_wait之前,线程B调用了sem_post,原子性地将value从-1加到0。因为之前的value是-1,线程B会触发futex_wake。
  3. 但此时线程A还没真正进入futex_wait的阻塞状态,所以futex_wake找不到等待的线程,返回0。
  4. 之后线程A执行futex_wait,但此时value已经是0,futex_wait会因为当前值不等于预期的-1而直接返回,不会阻塞——这其实是正确的逻辑,但你的断言会因为futex_wake返回0而触发失败。

POSIX信号量的sem_post不会暴露唤醒线程的数量,所以你看不到这个情况,但它的内部实现同样允许这种场景存在,只是没有断言而已。

修复方案

  1. 立即移除sem_post中对futex_wake返回值为1的断言:futex_wake的返回值是实际被唤醒的线程数,返回0是完全合法的,不代表你的实现有错误。
  2. 检查sem_wait的实现是否正确处理了竞态和虚假唤醒:正确的sem_wait需要用循环来确保最终能获取到信号量,比如:
typedef struct {
    int value;
} sem_t;

void sem_wait(sem_t* sem) {
    int prev = __atomic_sub_fetch(&sem->value, 1, __ATOMIC_SEQ_CST);
    if (prev > 0) {
        return;
    }
    // 循环处理虚假唤醒或竞态场景
    while (__atomic_load_n(&sem->value, __ATOMIC_SEQ_CST) <= 0) {
        int curr = __atomic_load_n(&sem->value, __ATOMIC_SEQ_CST);
        // 若信号量用于跨进程共享,需替换为FUTEX_WAIT
        syscall(SYS_futex, &sem->value, FUTEX_WAIT_PRIVATE, curr, NULL, NULL, 0);
    }
}

void sem_post(sem_t* sem) {
    int prev = __atomic_add_fetch(&sem->value, 1, __ATOMIC_SEQ_CST);
    // 只有之前的value小于0时,才可能有线程在等待
    if (prev < 0) {
        // 若信号量用于跨进程共享,需替换为FUTEX_WAKE
        syscall(SYS_futex, &sem->value, FUTEX_WAKE_PRIVATE, 1, NULL, NULL, 0);
    }
}

额外注意事项

  • 如果你是在跨进程共享内存中使用信号量,要把FUTEX_WAIT_PRIVATE和FUTEX_WAKE_PRIVATE换成FUTEX_WAIT和FUTEX_WAKE,PRIVATE版本只适用于同一进程内的线程。
  • 确保原子操作的内存序正确(比如__ATOMIC_SEQ_CST或者合适的ACQUIRE/RELEASE),否则可能出现内存可见性问题,导致线程看不到信号量的更新。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.25 03:25:18