使用Pthread并行计算连续数平均值出现随机结果的问题排查
并行计算平均值的线程异常问题及解决方法
问题描述
使用pthread.h库实现多线程并行计算1到1000连续数字的平均值,程序可正常编译但运行结果随机,存在多个线程处理同一子数组的情况,输出结果不稳定。
原代码
#include <stdio.h> #include <stdlib.h> #include <pthread.h> #include <string.h> #define INPUT_SIZE 1000 #define NUM_THREADS 5 #define ARR_SIZE INPUT_SIZE / NUM_THREADS struct sum_params { int size; int* data; }; int sum_array(struct sum_params* pt) { int size = pt->size; int* nums = pt->data; int sum = 0; for (int i = 0; i < size; i++) { sum += nums[i]; } return sum; } int main(int argc, char ** argv) { int arr_size = ARR_SIZE; if (INPUT_SIZE % NUM_THREADS != 0) { arr_size++; } int arr[NUM_THREADS][arr_size]; int n = 1; for (int i = 0; i < NUM_THREADS - 1; i++) { for (int j = 0; j < arr_size; j++) { arr[i][j] = n; n++; } } int rem = INPUT_SIZE - arr_size*(NUM_THREADS-1); for (int j = 0; j < rem; j++) { arr[NUM_THREADS-1][j] = n; n++; } for (int j = 0; j < (arr_size-rem); j++) { arr[NUM_THREADS-1][rem+j] = 0; } pthread_t threads[NUM_THREADS]; for (int i = 0; i < NUM_THREADS; i++) { struct sum_params params; params.size = arr_size; for (int j = 0; j < arr_size; j++) { params.data[j] = arr[i][j]; } pthread_create(&threads[i], NULL, (void* (*)(void*))(void*) sum_array, (void*) ¶ms); } int total = 0; for (int i = 0; i < NUM_THREADS; i++) { int thread_sum; pthread_join(threads[i], (void*) &thread_sum); printf("Thread Sum %d: %d\n", i, thread_sum); total += thread_sum; } double average = ((double) total) / INPUT_SIZE; printf("%f\n", average); pthread_exit(NULL); return 0; }
异常输出示例
Output 1: Thread Sum 0: 60100 Thread Sum 1: 140100 Thread Sum 2: 140100 Thread Sum 3: 180100 Thread Sum 4: 180100 700.500000 Output 2: Thread Sum 0: 100100 Thread Sum 1: 140100 Thread Sum 2: 140100 Thread Sum 3: 180100 Thread Sum 4: 180100 740.500000 Output 3: Thread Sum 0: 20100 Thread Sum 1: 140100 Thread Sum 2: 140100 Thread Sum 3: 180100 Thread Sum 4: 180100 660.500000
错误原因分析
- 局部参数生命周期冲突:创建线程的循环中,
struct sum_params params是栈上的局部变量,循环迭代时会被覆盖。线程启动后可能还未读取参数,params就被下一次循环修改,导致多个线程读取到相同的参数值。 - 非法内存访问:
params.data未初始化,直接执行params.data[j] = arr[i][j]属于写入未分配的内存,触发未定义行为,导致计算结果混乱。 - 线程返回值处理错误:
pthread_join的第二个参数要求是void**类型,用于接收线程返回的指针。直接传入(void*) &thread_sum会导致类型不匹配,无法正确获取线程的计算结果。
解决方法及修正代码
核心修正思路
- 为每个线程分配独立的堆内存参数,避免参数被覆盖。
- 直接计算每个线程负责的数字范围(起始/结束值),无需提前构建数组,减少内存开销和错误风险。
- 线程函数返回动态分配的结果,符合
pthread_join的返回值要求,确保结果正确传递。
修正后的代码
#include <stdio.h> #include <stdlib.h> #include <pthread.h> #define INPUT_SIZE 1000 #define NUM_THREADS 5 // 线程参数:存储负责计算的数字起始和结束值 struct thread_range { int start; int end; }; // 线程函数:计算[start, end]区间内数字的和 void* sum_range(void* arg) { struct thread_range* range = (struct thread_range*)arg; int sum = 0; for (int i = range->start; i <= range->end; i++) { sum += i; } // 动态分配内存存储结果,避免栈变量生命周期问题 int* result = malloc(sizeof(int)); *result = sum; // 释放参数内存 free(arg); return (void*)result; } int main() { pthread_t threads[NUM_THREADS]; int total = 0; // 计算每个线程的基础任务量和剩余任务量 int base_count = INPUT_SIZE / NUM_THREADS; int remainder = INPUT_SIZE % NUM_THREADS; // 创建线程并分配任务范围 for (int i = 0; i < NUM_THREADS; i++) { struct thread_range* range = malloc(sizeof(struct thread_range)); // 计算当前线程的起始数字 range->start = i * base_count + 1 + (i < remainder ? i : remainder); // 计算当前线程的结束数字 range->end = (i + 1) * base_count + (i < remainder ? i + 1 : remainder); pthread_create(&threads[i], NULL, sum_range, (void*)range); } // 等待线程完成并汇总结果 for (int i = 0; i < NUM_THREADS; i++) { void* thread_result; pthread_join(threads[i], &thread_result); int sum = *(int*)thread_result; printf("Thread Sum %d: %d\n", i, sum); total += sum; // 释放线程返回的结果内存 free(thread_result); } double average = (double)total / INPUT_SIZE; printf("Average: %f\n", average); return 0; }
修正要点说明
- 独立参数分配:每个线程的任务参数通过
malloc分配,确保每个线程拥有独立的参数空间,不会被其他线程或主线程循环覆盖。 - 直接范围计算:无需提前构建数组,通过计算起始和结束值直接求和,避免了数组填充和补0的额外操作,同时消除了无效值对求和的影响。
- 正确返回结果:线程函数使用
malloc分配结果内存,主线程接收后取出值再释放,完全符合pthread库的返回值规范,避免了类型不匹配导致的未定义行为。
内容的提问来源于stack exchange,提问作者T. Rex
相关产品推荐
相关产品推荐

