使用信号量的多线程自增程序未实现互斥的原因排查
多线程计数器竞态问题排查与修复
我尝试实现一个简单的多线程程序,让两个线程对共享计数器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
相关产品推荐
相关产品推荐

