pthread多线程程序异常输出分析(5种场景)及修复方案咨询
pthread多线程程序输出不稳定的原因及修复方法
输出产生多种结果的原因
原程序中所有子线程都接收了主线程变量i的内存地址,而非创建线程时i的副本。由于操作系统的线程调度具有不确定性,子线程执行时读取到的i值是主线程当前的实时值,而非创建线程时的初始值,从而导致多种不同输出。具体表现对应的核心原因包括:
- 线程调度时机不确定:操作系统可随时切换线程,主线程创建线程后可能继续执行
i++,子线程可能在i多次自增后才被调度,读取到更大的i值。例如主线程快速完成4次线程创建,所有子线程都在循环结束后执行,此时i已变为4,所有线程都会输出Thread number 4。 - 共享变量无同步机制:
i是主线程与子线程的共享变量,没有同步锁或其他机制保证子线程读取i时主线程不会修改它。比如线程0还未读取i,主线程已将i自增到1,线程0就会读取到1。 - 线程创建与执行的延迟:
pthread_create仅完成线程创建,子线程不会立即执行。主线程可能在子线程启动前完成多次循环,导致子线程读取到后续的i值。例如线程3创建后,主线程的i已变为4,线程3执行时就会输出4。 - 极端场景下的指令重排序:编译器或CPU可能对指令进行重排,虽然
printf是IO操作限制了重排,但极端情况下仍可能导致子线程读取到意外的i值。 - 多线程并发访问共享变量:
i++并非原子操作(分为读取、加1、写回三步),若主线程在执行i++的过程中切换到子线程,子线程可能读取到i的中间状态(虽然32位系统上int的读写通常是原子的,但调度问题仍会导致值不确定)。
以上原因共同导致程序可能出现多种输出,比如0、1、2、3(理想顺序)、1、1、3、4、0、2、2、3、4、4、4、4、0、0、0、0等。
修复方法
针对核心问题,我们需要让每个子线程获取创建时i的独立副本,而非共享的内存地址,以下是几种可行方案:
方案1:为每个线程分配独立内存存储i的副本
通过malloc为每个线程分配独立的内存空间,存储当前循环的i值,线程执行完毕后释放内存。这种方法兼容性最好,不依赖系统架构:
#include <stdio.h> #include <pthread.h> #include <stdlib.h> #define NUM_THREADS 4 void *threadFunc(void *pArg) { int myNum = *(int*)pArg; free(pArg); // 释放分配的内存 printf("Thread number %d\n", myNum); return NULL; } int main(void) { int i; pthread_t tid[NUM_THREADS]; for(i = 0; i < NUM_THREADS; i++) { int *p = malloc(sizeof(int)); *p = i; pthread_create(&tid[i], NULL, threadFunc, p); } for(i = 0; i < NUM_THREADS; i++) { pthread_join(tid[i], NULL); } return 0; }
方案2:直接传递i的值作为参数(简洁但依赖系统架构)
利用void*可以容纳int大小的特性(现代系统普遍满足),将i强制转换为void*传递给线程,线程内部再转换回int:
#include <stdio.h> #include <pthread.h> #define NUM_THREADS 4 void *threadFunc(void *pArg) { int myNum = (int)pArg; printf("Thread number %d\n", myNum); return NULL; } int main(void) { int i; pthread_t tid[NUM_THREADS]; for(i = 0; i < NUM_THREADS; i++) { pthread_create(&tid[i], NULL, threadFunc, (void*)i); } for(i = 0; i < NUM_THREADS; i++) { pthread_join(tid[i], NULL); } return 0; }
注意:若系统中int的大小超过void*(极端场景),这种方法会出错,因此方案1更通用。
方案3:使用同步机制强制线程串行读取(不推荐,损失并行性)
通过互斥锁和条件变量,让主线程等待子线程读取完i后再继续自增,这种方法会让线程串行执行,失去多线程并行优势,仅适用于特殊场景:
#include <stdio.h> #include <pthread.h> #define NUM_THREADS 4 pthread_mutex_t mutex; pthread_cond_t cond; int ready = 0; void *threadFunc(void *pArg) { int *p = (int*)pArg; pthread_mutex_lock(&mutex); ready = 1; pthread_cond_signal(&cond); int myNum = *p; pthread_mutex_unlock(&mutex); printf("Thread number %d\n", myNum); return NULL; } int main(void) { int i; pthread_t tid[NUM_THREADS]; pthread_mutex_init(&mutex, NULL); pthread_cond_init(&cond, NULL); for(i = 0; i < NUM_THREADS; i++) { pthread_mutex_lock(&mutex); ready = 0; pthread_create(&tid[i], NULL, threadFunc, &i); while(!ready) { pthread_cond_wait(&cond, &mutex); } pthread_mutex_unlock(&mutex); } for(i = 0; i < NUM_THREADS; i++) { pthread_join(tid[i], NULL); } pthread_mutex_destroy(&mutex); pthread_cond_destroy(&cond); return 0; }
内容的提问来源于stack exchange,提问作者mena
相关产品推荐
相关产品推荐

