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

Futex与Pthreads锁实现异常:sum计算结果不符预期求助

问题:Futex实现的锁导致线程同步错误,结果不符合预期

两个线程分别对全局变量sum执行1000000次加1和减1操作,预期最终sum值为0,但实际运行时结果时而正确时而错误,问题出在自定义的futex锁实现上。

原代码

#include <stdio.h>
#include <pthread.h>
#include <stdint.h>
#include <stdatomic.h>
#include <sys/syscall.h>
#include <linux/futex.h>
#include <unistd.h>
#include <errno.h>
#include <stdlib.h>

int64_t sum;
pthread_mutex_t mtx;

uint32_t ftx;

int futex(uint32_t *uaddr, int futex_op, uint32_t val, const struct timespec *timeout, uint32_t *uaddr2, uint32_t val3)
{
    return syscall(SYS_futex, uaddr, futex_op, val, timeout, uaddr2, val3);
}

void fwait(uint32_t *futx)
{
    long st;
    uint32_t one = 1;
    
    do {
        if (atomic_compare_exchange_strong(futx, &one, 0))
            break;
        
        st = futex(futx, FUTEX_WAIT_PRIVATE, 0, NULL, NULL, 0);
        
        if ((st == -1) && (errno != EAGAIN)) {
            printf("error-wait-futex\n");
            exit(1);
        }
    } while (1);
    
}

void fwake(uint32_t *futx)
{
    long st;
    uint32_t zero = 0;
    
    if (atomic_compare_exchange_strong(futx, &zero, 1)) {
        st = futex(futx, FUTEX_WAKE_PRIVATE, 1, NULL, NULL, 0);
        if (st == -1) {
            printf("error-wake-futex\n");
            exit(1);
        }
    }
}

void lock(void)
{
    fwait(&ftx);
}

void unlock(void)
{
    fwake(&ftx);
}

void * t1_handler(void *arg)
{
    int mod;
    uint64_t x;
    
    mod = *(int *)arg;
    
    x = 0;
    while (x < 1000000) {
        lock();
        sum += mod;
        x++;
        unlock();
    }
}

void proc(void)
{
    pthread_t t1, t2;
    int a1, a2;
    
    sum = 0;
    ftx = 1;
    
    a1 = 1;
    a2 = -1;
    
    pthread_mutex_init(&mtx, NULL);
    pthread_create(&t1, NULL, t1_handler, &a1);
    pthread_create(&t2, NULL, t1_handler, &a2);
    
    pthread_join(t1, NULL);
    pthread_join(t2, NULL);

    printf("sum: %lld\n", sum);
}

int main(void)
{
    proc();
    return 0;
}

错误原因分析

1. fwait函数的CAS逻辑错误

atomic_compare_exchange_strong的行为是:如果目标地址的值等于预期值,就将其替换为目标值并返回true;否则,会把预期变量的值更新为目标地址的当前值并返回false。

原代码中one变量只在函数开头初始化一次为1,当第一次CAS失败(此时ftx为0),one会被自动修改为0。后续循环中,CAS操作变成了尝试将ftx从0改为0——这必然成功,导致线程无需等待直接进入临界区,多个线程同时操作sum,引发竞态条件,最终结果错误。

2. fwake函数的解锁逻辑冗余且存在风险

只有持有锁的线程才会调用unlock,此时ftx必然是0(获取锁时会将其设为0),不需要用atomic_compare_exchange_strong来检查状态。如果CAS失败(理论上不该发生,但极端情况可能触发),会跳过唤醒操作,导致等待线程永远阻塞。

修复后的代码

#include <stdio.h>
#include <pthread.h>
#include <stdint.h>
#include <stdatomic.h>
#include <sys/syscall.h>
#include <linux/futex.h>
#include <unistd.h>
#include <errno.h>
#include <stdlib.h>

int64_t sum;
pthread_mutex_t mtx;

uint32_t ftx;

int futex(uint32_t *uaddr, int futex_op, uint32_t val, const struct timespec *timeout, uint32_t *uaddr2, uint32_t val3)
{
    return syscall(SYS_futex, uaddr, futex_op, val, timeout, uaddr2, val3);
}

void fwait(uint32_t *futx)
{
    long st;
    uint32_t one;
    
    do {
        one = 1; // 每次循环重置预期值,避免被CAS修改后逻辑混乱
        if (atomic_compare_exchange_strong(futx, &one, 0))
            break;
        
        st = futex(futx, FUTEX_WAIT_PRIVATE, 0, NULL, NULL, 0);
        
        if ((st == -1) && (errno != EAGAIN)) {
            printf("error-wait-futex\n");
            exit(1);
        }
    } while (1);
    
}

void fwake(uint32_t *futx)
{
    long st;
    atomic_store(futx, 1); // 直接将锁设为可用状态,无需CAS检查
    st = futex(futx, FUTEX_WAKE_PRIVATE, 1, NULL, NULL, 0);
    if (st == -1) {
        printf("error-wake-futex\n");
        exit(1);
    }
}

void lock(void)
{
    fwait(&ftx);
}

void unlock(void)
{
    fwake(&ftx);
}

void * t1_handler(void *arg)
{
    int mod;
    uint64_t x;
    
    mod = *(int *)arg;
    
    x = 0;
    while (x < 1000000) {
        lock();
        sum += mod;
        x++;
        unlock();
    }
}

void proc(void)
{
    pthread_t t1, t2;
    int a1, a2;
    
    sum = 0;
    ftx = 1;
    
    a1 = 1;
    a2 = -1;
    
    pthread_mutex_init(&mtx, NULL);
    pthread_create(&t1, NULL, t1_handler, &a1);
    pthread_create(&t2, NULL, t1_handler, &a2);
    
    pthread_join(t1, NULL);
    pthread_join(t2, NULL);

    printf("sum: %lld\n", sum);
}

int main(void)
{
    proc();
    return 0;
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.25 21:12:29