如何解决多线程竞态条件?不用sleep如何保证全局变量var输出稳定
问题根源
你遇到的现象是典型的线程执行顺序不确定导致的竞态条件:调用pthread_create仅会向操作系统注册新的可调度线程,并不会保证新线程会立刻抢占主线程的CPU时间片执行,所以var的取值完全取决于主线程执行printf("var=%d",var);和新线程执行var = 1;的先后调度顺序,自然会出现两种结果。
不使用sleep的鲁棒解决方案
有三种常用的工业级实现方式,都是靠线程同步原语保证执行顺序,不会依赖不确定的调度延迟:
方案1:调整pthread_join的调用位置
pthread_join本身就会阻塞主线程,直到目标线程执行结束。只要把打印var的逻辑放到pthread_join之后,就可以100%保证拿到线程修改后的结果:
#include <stdio.h> #include <stdlib.h> #include <pthread.h> int var = -1; // 全局变量 void *myThreadFun(void *vargp) { var = 1; return NULL; } int main() { pthread_t thread_id; printf("Before Thread\n"); pthread_create(&thread_id, NULL, myThreadFun, NULL); // 先等待线程执行结束再读取var pthread_join(thread_id, NULL); printf("var=%d\n",var); printf("After Thread\n"); exit(0); }
适用场景:不需要在主线程中做其他并行操作,只需要拿到线程执行后的结果。
方案2:使用条件变量做执行状态同步
如果主线程在创建新线程之后还有其他并行任务要做,不能直接阻塞等线程结束,可以用条件变量+互斥锁的组合,标记线程的执行状态:
#include <stdio.h> #include <stdlib.h> #include <pthread.h> int var = -1; // 全局变量 int thread_done = 0; // 线程执行完成标记 pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; pthread_cond_t cond = PTHREAD_COND_INITIALIZER; void *myThreadFun(void *vargp) { pthread_mutex_lock(&mutex); var = 1; thread_done = 1; pthread_cond_signal(&cond); // 通知主线程状态更新 pthread_mutex_unlock(&mutex); return NULL; } int main() { pthread_t thread_id; printf("Before Thread\n"); pthread_create(&thread_id, NULL, myThreadFun, NULL); // 这里可以插入主线程需要并行执行的其他代码 // 等待线程执行完成的信号 pthread_mutex_lock(&mutex); while(!thread_done) { pthread_cond_wait(&cond, &mutex); } printf("var=%d\n",var); pthread_mutex_unlock(&mutex); pthread_join(thread_id, NULL); printf("After Thread\n"); exit(0); }
适用场景:主线程有并行任务需要执行,不需要立刻等待线程结束,可以灵活控制等待时机。
方案3:使用POSIX信号量做同步
信号量的逻辑更简单,适合做单对单的执行顺序同步:
#include <stdio.h> #include <stdlib.h> #include <pthread.h> #include <semaphore.h> int var = -1; // 全局变量 sem_t sem; void *myThreadFun(void *vargp) { var = 1; sem_post(&sem); // 发送信号量,通知主线程修改完成 return NULL; } int main() { pthread_t thread_id; sem_init(&sem, 0, 0); // 初始化信号量初始值为0 printf("Before Thread\n"); pthread_create(&thread_id, NULL, myThreadFun, NULL); // 这里可以插入主线程需要并行执行的其他代码 sem_wait(&sem); // 阻塞等待信号量,直到线程修改完成 printf("var=%d\n",var); pthread_join(thread_id, NULL); sem_destroy(&sem); printf("After Thread\n"); exit(0); }
适用场景:轻量级同步场景,代码比条件变量更简洁。
内容的提问来源于stack exchange,提问作者Learner09
相关产品推荐
相关产品推荐

