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

使用信号量的多线程自增程序未实现互斥的原因排查

多线程计数器竞态问题排查与修复

我尝试实现一个简单的多线程程序,让两个线程对共享计数器cnt各执行n次自增操作,用信号量保证互斥避免竞态条件,但程序运行后cnt的值经常不等于预期的2*n,怀疑互斥逻辑有问题。

问题代码

#include <stdio.h>
#include <pthread.h>
#include <stdlib.h>
#include <semaphore.h>

volatile long cnt = 0;
sem_t mutex;

void *thread(void *vargp) 
{
    long n = *((long *)vargp);
    for (long i = 0; i < n; i++) 
    {
        sem_wait(&mutex);
        cnt++;
        sem_post(&mutex);
    }
    return NULL;
}

int main(int argc, char **argv) 
{
    sem_init(&mutex, 0, 1);
    long n = atoi(argv[1]);

    pthread_t tid1, tid2;
    pthread_create(&tid1, NULL, thread, &n);
    pthread_create(&tid2, NULL, thread, &n);
    pthread_join(tid1, NULL);
    pthread_join(tid2, NULL);

    if (cnt != 2*n) printf("BOOM! cnt=%ld\n", cnt);
    else            printf("OK    cnt=%ld\n", cnt);

    sem_destroy(&mutex);
    exit(0);
}

编译命令

gcc -pthread -o goodcnt goodcnt.c

编译器版本:Apple clang 16.0.0

运行异常结果

$ ./goodcnt 100000                 
BOOM! cnt=199999
$ ./goodcnt 100000
BOOM! cnt=199994
$ ./goodcnt 100000
BOOM! cnt=199987
$ ./goodcnt 100000
BOOM! cnt=199994
$ ./goodcnt 100000
BOOM! cnt=199996

问题根源与修复方案

问题本质

信号量的互斥逻辑本身是正确的,问题出在线程参数传递的竞态上:主线程将局部变量n的地址传给两个线程,但pthread_create是异步调用,主线程可能还没完成n = atoi(argv[1]);的赋值操作,线程就已经开始读取*((long *)vargp),导致线程拿到错误的n值(比如变量初始化前的垃圾值),最终自增次数不足,使得cnt总和小于预期的2*n。

修复方案

方案1:直接传递值(推荐)

利用void*的宽度兼容特性,直接把n的值转为void*传递,避免指针访问的竞态:

// 主线程修改创建逻辑
pthread_create(&tid1, NULL, thread, (void*)n);
pthread_create(&tid2, NULL, thread, (void*)n);

// 线程函数修改参数读取逻辑
void *thread(void *vargp) 
{
    long n = (long)vargp;
    // 原循环逻辑不变
}

方案2:使用动态分配内存

如果必须传递指针,用malloc分配内存存储n的值,确保线程读取时数据已稳定:

// 主线程中
long *n_ptr = malloc(sizeof(long));
*n_ptr = atoi(argv[1]);
pthread_create(&tid1, NULL, thread, n_ptr);
pthread_create(&tid2, NULL, thread, n_ptr);

// 线程函数保持原读取逻辑
void *thread(void *vargp) 
{
    long n = *((long *)vargp);
    // 原循环逻辑不变
}

// 主线程join后释放内存
pthread_join(tid1, NULL);
pthread_join(tid2, NULL);
free(n_ptr);

修复后运行程序,./goodcnt 100000会稳定输出OK cnt=200000。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.14 12:21:00